cleanup code and cargo fmt

This commit is contained in:
tezlm 2023-10-04 15:10:53 -07:00
parent 8f06f8a502
commit 2392087c4b
Signed by: tezlm
GPG key ID: 649733FCD94AFBBA
12 changed files with 353 additions and 193 deletions

36
print3.wat Normal file
View file

@ -0,0 +1,36 @@
;; testing offsets and such
(module
;; import from wasi
;; fn fd_write(fd, *iovs, iovs_len, nwritten) -> bytes_written
(import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32)))
;; create memory (size = 1 page = 64KiB)
(memory $foobar 1)
;; export memory - it's required, but we don't use it so the size is set to 0
(export "memory" (memory 0))
;; write string to memory (offset = 8 bytes)
(data (i32.const 20) "Hello, world!\n")
(func $main (export "_start")
;; iov.iov_base - pointer to string (offset = 0 bytes)
;; the string's offset is 8 bytes in memory
(i32.store (i32.const 12) (i32.const 20))
;; iov.iov_len - length of the hello world string (offset = 4 bytes)
;; the string's length is 14 bytes
(i32.store (i32.const 16) (i32.const 24))
(i32.const 1)
(call $fd_write
;; (i32.const 1) ;; fd: stdout = 1
(i32.const 12) ;; data: pointer to memory - this is the first memory we create (index 0)
(i32.const 1) ;; data_len: there's 1 string
(i32.const 2468) ;; nwritten: i don't care about this, write it wherever
)
drop ;; drop number of bytes written
)
)

View file

@ -17,7 +17,7 @@ use crate::Error;
/// pipeline to convert parsed tree -> wat/wasm /// pipeline to convert parsed tree -> wat/wasm
pub struct Compiler<'a> { pub struct Compiler<'a> {
output: Box<&'a mut dyn std::io::Write>, output: &'a mut dyn std::io::Write,
} }
struct Allocator { struct Allocator {
@ -44,10 +44,8 @@ struct Allocator {
// "#; // "#;
impl<'a> Compiler<'a> { impl<'a> Compiler<'a> {
pub fn new(output: Box<&mut dyn Write>, _output_format: OutputFormat) -> Compiler { pub fn new(output: &mut dyn Write, _output_format: OutputFormat) -> Compiler {
Compiler { Compiler { output }
output,
}
} }
pub fn write_module(&mut self, stmts: &[Statement]) -> Result<(), Error> { pub fn write_module(&mut self, stmts: &[Statement]) -> Result<(), Error> {
@ -60,7 +58,12 @@ impl<'a> Compiler<'a> {
let mut gctx = GenContext::new(); let mut gctx = GenContext::new();
let mut alloc = Allocator::new(); let mut alloc = Allocator::new();
get_locals(&Expr::Block(crate::data::Block(stmts.to_vec())), &mut alloc, &mut ctx, &mut gctx); get_locals(
&Expr::Block(crate::data::Block(stmts.to_vec())),
&mut alloc,
&mut ctx,
&mut gctx,
);
// get_strings(&Expr::Block(crate::data::Block(stmts.to_vec())), &mut gctx); // get_strings(&Expr::Block(crate::data::Block(stmts.to_vec())), &mut gctx);
// if !gctx.strings.is_empty() { // if !gctx.strings.is_empty() {
// writeln!(self.output, "(memory 1)")?; // writeln!(self.output, "(memory 1)")?;
@ -72,7 +75,7 @@ impl<'a> Compiler<'a> {
for stmt in stmts { for stmt in stmts {
match stmt { match stmt {
Statement::Func(func) => self.write_func(&gctx, &ctx, func)?, Statement::Func(func) => self.write_func(&gctx, &ctx, func)?,
Statement::Let(..) | Statement::TailExpr(..) | Statement::Expr (..) => { Statement::Let(..) | Statement::TailExpr(..) | Statement::Expr(..) => {
return Err(Error::OtherError("incorrect top level statement".into())) return Err(Error::OtherError("incorrect top level statement".into()))
} }
}; };
@ -81,7 +84,12 @@ impl<'a> Compiler<'a> {
Ok(()) Ok(())
} }
fn write_func(&mut self, parent_gctx: &GenContext, ctx: &Context, func: &Func) -> Result<(), Error> { fn write_func(
&mut self,
parent_gctx: &GenContext,
ctx: &Context,
func: &Func,
) -> Result<(), Error> {
write!(self.output, "(func ${}", func.name)?; write!(self.output, "(func ${}", func.name)?;
if func.name == "main" { if func.name == "main" {
@ -139,12 +147,19 @@ impl<'a> Compiler<'a> {
Ok(()) Ok(())
} }
fn gen_expr(&mut self, expr: &Expr, parent_gctx: &GenContext, ctx: &Context) -> Result<(), Error> { fn gen_expr(
&mut self,
expr: &Expr,
parent_gctx: &GenContext,
ctx: &Context,
) -> Result<(), Error> {
match expr { match expr {
Expr::Literal(lit) => match lit { Expr::Literal(lit) => match lit {
Literal::Integer(int) => writeln!(self.output, "i32.const {int}")?, Literal::Integer(int) => writeln!(self.output, "i32.const {int}")?,
Literal::Float(f) => writeln!(self.output, "f64.const {f}")?, Literal::Float(f) => writeln!(self.output, "f64.const {f}")?,
Literal::Boolean(b) => writeln!(self.output, "i32.const {}", if *b { 1 } else { 0 })?, Literal::Boolean(b) => {
writeln!(self.output, "i32.const {}", if *b { 1 } else { 0 })?
}
// Literal::String(s) => writeln!(self.output, "i32.const {}", parent_gctx.strings.iter().find_map(|(s2, off)| (s == s2).then_some(off)).unwrap())?, // Literal::String(s) => writeln!(self.output, "i32.const {}", parent_gctx.strings.iter().find_map(|(s2, off)| (s == s2).then_some(off)).unwrap())?,
_ => todo!(), _ => todo!(),
}, },
@ -210,8 +225,12 @@ impl<'a> Compiler<'a> {
match pat { match pat {
Pattern::Literal(lit) => { Pattern::Literal(lit) => {
match lit { match lit {
Literal::Integer(int) => writeln!(self.output, "i32.const {}", int)?, Literal::Integer(int) => {
Literal::Boolean(b) => writeln!(self.output, "i32.const {}", if *b { 1 } else { 0 })?, writeln!(self.output, "i32.const {}", int)?
}
Literal::Boolean(b) => {
writeln!(self.output, "i32.const {}", if *b { 1 } else { 0 })?
}
_ => todo!(), _ => todo!(),
} }
writeln!(self.output, "local.get $match")?; writeln!(self.output, "local.get $match")?;
@ -271,9 +290,7 @@ impl<'a> Compiler<'a> {
// FIXME: not sure what i was thinking // FIXME: not sure what i was thinking
impl Allocator { impl Allocator {
fn new() -> Allocator { fn new() -> Allocator {
Allocator { Allocator { count: 0 }
count: 0,
}
} }
fn alloc_ident(&mut self, name: &str) -> String { fn alloc_ident(&mut self, name: &str) -> String {
@ -313,8 +330,11 @@ fn get_locals(expr: &Expr, alloc: &mut Allocator, ctx: &mut Context, gctx: &mut
} }
Statement::TailExpr(expr) => get_locals(expr, alloc, ctx, gctx), Statement::TailExpr(expr) => get_locals(expr, alloc, ctx, gctx),
Statement::Expr(expr) => get_locals(expr, alloc, ctx, gctx), Statement::Expr(expr) => get_locals(expr, alloc, ctx, gctx),
Statement::Func(Func { name, ret, params, .. }) => { Statement::Func(Func {
ctx.funcs.insert(name.clone(), (params.clone(), ret.clone())); name, ret, params, ..
}) => {
ctx.funcs
.insert(name.clone(), (params.clone(), ret.clone()));
} }
} }
} }
@ -325,7 +345,11 @@ fn get_locals(expr: &Expr, alloc: &mut Allocator, ctx: &mut Context, gctx: &mut
get_locals(b, alloc, ctx, gctx); get_locals(b, alloc, ctx, gctx);
} }
Expr::Literal(Literal::String(s)) => { Expr::Literal(Literal::String(s)) => {
let offset = gctx.strings.last().map(|(s, off)| off + s.len()).unwrap_or(8); let offset = gctx
.strings
.last()
.map(|(s, off)| off + s.len())
.unwrap_or(8);
gctx.strings.push((s.clone(), offset)); gctx.strings.push((s.clone(), offset));
} }
Expr::Match(pat, arms) => { Expr::Match(pat, arms) => {

View file

@ -1,6 +1,6 @@
// TODO: TypedStatement and TypedExpression? // TODO: TypedStatement and TypedExpression?
use crate::lexer::{TokenContent, Symbol}; use crate::lexer::{Symbol, TokenContent};
#[rustfmt::skip] #[rustfmt::skip]
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -175,7 +175,7 @@ impl Type {
Self::Tuple(v) => { Self::Tuple(v) => {
let s: Vec<_> = v.iter().map(Type::string).collect(); let s: Vec<_> = v.iter().map(Type::string).collect();
format!("({})", s.join(", ")) format!("({})", s.join(", "))
}, }
_ => unimplemented!(), _ => unimplemented!(),
} }
} }

View file

@ -1,4 +1,4 @@
use miette::{Diagnostic, SourceSpan, NamedSource}; use miette::{Diagnostic, NamedSource, SourceSpan};
use thiserror::Error; use thiserror::Error;
#[derive(Debug, Error, Diagnostic)] #[derive(Debug, Error, Diagnostic)]
@ -40,7 +40,6 @@ pub struct TypeWrapper {
pub struct SyntaxError { pub struct SyntaxError {
// #[source_code] // #[source_code]
// pub src: NamedSource, // pub src: NamedSource,
#[help] #[help]
pub help: String, pub help: String,

View file

@ -3,7 +3,7 @@ use crate::Error;
/// helper pipeline to convert wat/wasm -> actual text/binary /// helper pipeline to convert wat/wasm -> actual text/binary
pub struct Generator<'a> { pub struct Generator<'a> {
output: Box<&'a mut dyn std::io::Write>, output: &'a mut dyn std::io::Write,
format: OutputFormat, format: OutputFormat,
} }
@ -24,11 +24,8 @@ impl OutputFormat {
} }
impl Generator<'_> { impl Generator<'_> {
pub fn new(output: Box<&mut dyn std::io::Write>, format: OutputFormat) -> Generator { pub fn new(output: &mut dyn std::io::Write, format: OutputFormat) -> Generator {
Generator { Generator { output, format }
output,
format,
}
} }
pub fn write_module(&mut self) -> Result<(), Error> { pub fn write_module(&mut self) -> Result<(), Error> {

View file

@ -4,8 +4,6 @@ use crate::Error;
pub struct Lexer { pub struct Lexer {
input: Vec<char>, input: Vec<char>,
pos: usize, pos: usize,
name: String,
src: String,
} }
#[rustfmt::skip] #[rustfmt::skip]
@ -51,12 +49,10 @@ pub enum Symbol {
} }
impl Lexer { impl Lexer {
pub fn new(input: &str, name: &str) -> Lexer { pub fn new(input: &str) -> Lexer {
Lexer { Lexer {
input: input.chars().collect(), input: input.chars().collect(),
pos: 0, pos: 0,
src: input.to_string(),
name: name.to_string(),
} }
} }
@ -73,7 +69,10 @@ impl Lexer {
.get(self.pos) .get(self.pos)
.is_some_and(|c| c.is_ascii_alphanumeric()) .is_some_and(|c| c.is_ascii_alphanumeric())
{ {
return Err(Error::syn((self.pos, 1), "there shouldn't be extra characters after the number")); return Err(Error::syn(
(self.pos, 1),
"there shouldn't be extra characters after the number",
));
} }
token token
} }
@ -81,7 +80,10 @@ impl Lexer {
self.pos += 1; self.pos += 1;
let ch = self.lex_char()?; let ch = self.lex_char()?;
if !self.input.get(self.pos).is_some_and(|c| *c == '\'') { if !self.input.get(self.pos).is_some_and(|c| *c == '\'') {
return Err(Error::syn((start, self.pos - start), "close your character with a '")); return Err(Error::syn(
(start, self.pos - start),
"close your character with a '",
));
} }
self.pos += 1; self.pos += 1;
TokenContent::Char(ch) TokenContent::Char(ch)
@ -111,7 +113,10 @@ impl Lexer {
} }
_ => self.lex_op()?, _ => self.lex_op()?,
}; };
Ok(Some(Token { token: tok, span: (start, self.pos - start) })) Ok(Some(Token {
token: tok,
span: (start, self.pos - start),
}))
} }
fn lex_number(&mut self) -> Result<TokenContent, Error> { fn lex_number(&mut self) -> Result<TokenContent, Error> {
@ -144,7 +149,10 @@ impl Lexer {
buffer.push(ch); buffer.push(ch);
self.pos += 1; self.pos += 1;
} else if ch.is_ascii_digit() { } else if ch.is_ascii_digit() {
return Err(Error::syn((self.pos, 1), "you seem to have used the wrong base for this number")); return Err(Error::syn(
(self.pos, 1),
"you seem to have used the wrong base for this number",
));
} else { } else {
break; break;
} }
@ -182,10 +190,12 @@ impl Lexer {
let ch = match self.input.get(self.pos) { let ch = match self.input.get(self.pos) {
Some('\\') => { Some('\\') => {
self.pos += 1; self.pos += 1;
let ch = self let ch = self.input.get(self.pos).ok_or_else(|| {
.input Error::syn(
.get(self.pos) (self.pos, 1),
.ok_or_else(|| Error::syn((self.pos, 1), "there should be an character here (which will be escaped)"))?; "there should be an character here (which will be escaped)",
)
})?;
match ch { match ch {
'n' => '\n', 'n' => '\n',
't' => '\t', 't' => '\t',
@ -193,11 +203,23 @@ impl Lexer {
'"' => '\"', '"' => '\"',
// 'x' => '\x', // 'x' => '\x',
// 'u' => '\u', // 'u' => '\u',
_ => return Err(Error::syn((self.pos - 1, 2), &format!("i only know how to escape \\n, \\t, \\\\, or \\\", not \\{ch}"))), _ => {
return Err(Error::syn(
(self.pos - 1, 2),
format!(
"i only know how to escape \\n, \\t, \\\\, or \\\", not \\{ch}"
),
))
}
} }
} }
Some(ch) => *ch, Some(ch) => *ch,
None => return Err(Error::syn((self.pos, 1), "there should be a character here, not an eof")), None => {
return Err(Error::syn(
(self.pos, 1),
"there should be a character here, not an eof",
))
}
}; };
self.pos += 1; self.pos += 1;
Ok(ch) Ok(ch)
@ -360,7 +382,12 @@ impl Lexer {
',' => Symbol::Comma, ',' => Symbol::Comma,
';' => Symbol::Semicolon, ';' => Symbol::Semicolon,
'?' => Symbol::Question, '?' => Symbol::Question,
_ => return Err(Error::syn((self.pos, self.pos), "unexpected character here")), _ => {
return Err(Error::syn(
(self.pos, self.pos),
"unexpected character here",
))
}
}; };
self.pos += 1; self.pos += 1;
Ok(TokenContent::Symbol(symbol)) Ok(TokenContent::Symbol(symbol))

View file

@ -1,8 +1,8 @@
#![allow(dead_code, clippy::single_match, clippy::only_used_in_recursion)] #![allow(dead_code, clippy::single_match, clippy::only_used_in_recursion)]
pub mod compiler;
pub mod data; pub mod data;
pub mod error; pub mod error;
pub mod compiler;
pub mod generator; pub mod generator;
pub mod lexer; pub mod lexer;
pub mod parser; pub mod parser;

View file

@ -4,10 +4,15 @@ a second time when generating (so the types are known), there should be
a better way a better way
*/ */
use std::{path::PathBuf, fs::OpenOptions}; use std::{fs::OpenOptions, path::PathBuf};
use clap::Parser; use clap::Parser;
use lang::{compiler::Compiler, generator::OutputFormat, lexer, parser, error::{SyntaxWrapper, TypeWrapper}}; use lang::{
compiler::Compiler,
error::{SyntaxWrapper, TypeWrapper},
generator::OutputFormat,
lexer, parser,
};
use miette::NamedSource; use miette::NamedSource;
#[derive(Debug, clap::Parser)] #[derive(Debug, clap::Parser)]
@ -31,7 +36,6 @@ enum Cli {
/// The file containing the code you want to compile /// The file containing the code you want to compile
file: PathBuf, file: PathBuf,
}, },
// future work: // future work:
// //
// /// Start a read-print-eval loop // /// Start a read-print-eval loop
@ -59,7 +63,8 @@ enum Cli {
#[derive(Debug, Clone, clap::ValueEnum)] #[derive(Debug, Clone, clap::ValueEnum)]
enum OutputType { enum OutputType {
Wat, Wasm, Wat,
Wasm,
} }
fn main() { fn main() {
@ -93,10 +98,10 @@ fn main() {
}; };
let reporter = miette::GraphicalReportHandler::new().with_theme(theme); let reporter = miette::GraphicalReportHandler::new().with_theme(theme);
match args { match &args {
Cli::Run { file } => { Cli::Run { file } | Cli::Compile { file, .. } => {
let source = std::fs::read_to_string(&file).expect("no source!"); let source = std::fs::read_to_string(file).expect("no source!");
let lexer = lexer::Lexer::new(&source, file.to_str().unwrap()); let lexer = lexer::Lexer::new(&source);
let tokens = match lexer.collect::<Result<Vec<_>, _>>() { let tokens = match lexer.collect::<Result<Vec<_>, _>>() {
Ok(tokens) => tokens, Ok(tokens) => tokens,
@ -105,7 +110,7 @@ fn main() {
lang::Error::SyntaxError(syn) => { lang::Error::SyntaxError(syn) => {
let mut s = String::new(); let mut s = String::new();
let syn = SyntaxWrapper { let syn = SyntaxWrapper {
src: NamedSource::new(file.to_string_lossy().to_string(), source), src: NamedSource::new(file.to_string_lossy(), source),
syn: vec![syn], syn: vec![syn],
}; };
@ -118,10 +123,10 @@ fn main() {
} }
}; };
let mut parser = parser::Parser::new(tokens, source.clone(), file.to_string_lossy().to_string()); let mut parser = parser::Parser::new(tokens);
let mut statements = vec![]; let mut statements = vec![];
loop { loop {
match parser.next() { match parser.parse_statement_option() {
Ok(None) => break, Ok(None) => break,
Ok(Some(tree)) => statements.push(tree), Ok(Some(tree)) => statements.push(tree),
Err(error) => { Err(error) => {
@ -129,7 +134,7 @@ fn main() {
lang::Error::SyntaxError(syn) => { lang::Error::SyntaxError(syn) => {
let mut s = String::new(); let mut s = String::new();
let syn = SyntaxWrapper { let syn = SyntaxWrapper {
src: NamedSource::new(file.to_string_lossy().to_string(), source), src: NamedSource::new(file.to_string_lossy(), source),
syn: vec![syn], syn: vec![syn],
}; };
reporter.render_report(&mut s, &syn).unwrap(); reporter.render_report(&mut s, &syn).unwrap();
@ -138,7 +143,7 @@ fn main() {
lang::Error::TypeError(ty) => { lang::Error::TypeError(ty) => {
let mut s = String::new(); let mut s = String::new();
let ty = TypeWrapper { let ty = TypeWrapper {
src: NamedSource::new(file.to_string_lossy().to_string(), source), src: NamedSource::new(file.to_string_lossy(), source),
ty: vec![ty], ty: vec![ty],
}; };
reporter.render_report(&mut s, &ty).unwrap(); reporter.render_report(&mut s, &ty).unwrap();
@ -151,8 +156,10 @@ fn main() {
} }
} }
match args {
Cli::Run { .. } => {
let mut wat = vec![]; let mut wat = vec![];
let mut gen = Compiler::new(Box::new(&mut wat), OutputFormat::WatVerbose); let mut gen = Compiler::new(&mut wat, OutputFormat::WatVerbose);
if let Err(err) = gen.write_module(&statements) { if let Err(err) = gen.write_module(&statements) {
panic!("{:?}", err); panic!("{:?}", err);
@ -164,34 +171,16 @@ fn main() {
let linker = wasmtime::Linker::new(&engine); let linker = wasmtime::Linker::new(&engine);
let mut store = wasmtime::Store::new(&engine, 4); let mut store = wasmtime::Store::new(&engine, 4);
let instance = linker.instantiate(&mut store, &module).unwrap(); let instance = linker.instantiate(&mut store, &module).unwrap();
let start = instance.get_typed_func::<(), i32>(&mut store, "_start").unwrap(); let start = instance
.get_typed_func::<(), i32>(&mut store, "_start")
.unwrap();
dbg!(start.call(&mut store, ()).unwrap()); dbg!(start.call(&mut store, ()).unwrap());
} }
Cli::Compile { file, format, output } => { Cli::Compile {
let source = std::fs::read_to_string(&file).expect("no source!"); format,
let lexer = lexer::Lexer::new(&source, file.to_str().unwrap()); output,
file,
let tokens = match lexer.collect::<Result<Vec<_>, _>>() { } => {
Ok(tokens) => tokens,
Err(error) => {
eprintln!("error: {:?}", error);
return
}
};
let mut parser = parser::Parser::new(tokens, source.clone(), file.to_string_lossy().to_string());
let mut statements = vec![];
loop {
match parser.next() {
Ok(None) => break,
Ok(Some(tree)) => statements.push(tree),
Err(error) => {
eprintln!("error: {:?}", error);
return;
}
}
}
let format = format.unwrap_or_else(|| { let format = format.unwrap_or_else(|| {
if file.extension().is_some_and(|ext| ext == "wasm") { if file.extension().is_some_and(|ext| ext == "wasm") {
OutputType::Wasm OutputType::Wasm
@ -201,20 +190,26 @@ fn main() {
}); });
let mut output: Box<dyn std::io::Write> = match output { let mut output: Box<dyn std::io::Write> = match output {
Some(path) => Box::new(OpenOptions::new().create(true).write(true).open(path).expect("failed to open file")), Some(path) => Box::new(
OpenOptions::new()
.create(true)
.write(true)
.open(path)
.expect("failed to open file"),
),
None => Box::new(std::io::stdout()), None => Box::new(std::io::stdout()),
}; };
match format { match format {
OutputType::Wat => { OutputType::Wat => {
let mut gen = Compiler::new(Box::new(&mut output), OutputFormat::WatVerbose); let mut gen = Compiler::new(&mut output, OutputFormat::WatVerbose);
if let Err(err) = gen.write_module(&statements) { if let Err(err) = gen.write_module(&statements) {
panic!("{:?}", err); panic!("{:?}", err);
} }
} }
OutputType::Wasm => { OutputType::Wasm => {
let mut gen = Compiler::new(Box::new(&mut output), OutputFormat::Wasm); let mut gen = Compiler::new(&mut output, OutputFormat::Wasm);
if let Err(err) = gen.write_module(&statements) { if let Err(err) = gen.write_module(&statements) {
panic!("{:?}", err); panic!("{:?}", err);
@ -223,4 +218,6 @@ fn main() {
} }
} }
} }
}
}
} }

View file

@ -1,14 +1,12 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::data::{BinaryOp, Block, Expr, Literal, Pattern, PrefixOp, Statement, Type, Func}; use crate::data::{BinaryOp, Block, Expr, Func, Literal, Pattern, PrefixOp, Statement, Type};
use crate::lexer::{TokenContent, Symbol, Token}; use crate::lexer::{Symbol, Token, TokenContent};
use crate::Error; use crate::Error;
pub struct Parser { pub struct Parser {
tokens: Vec<Token>, tokens: Vec<Token>,
pos: usize, pos: usize,
src: String,
name: String,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -18,13 +16,8 @@ pub struct Context {
} }
impl Parser { impl Parser {
pub fn new(tokens: Vec<Token>, src: String, name: String) -> Parser { pub fn new(tokens: Vec<Token>) -> Parser {
Parser { Parser { tokens, pos: 0 }
src,
name,
tokens,
pos: 0,
}
} }
fn err_syn(&self, pos: (usize, usize), help: &str) -> Error { fn err_syn(&self, pos: (usize, usize), help: &str) -> Error {
@ -32,7 +25,10 @@ impl Parser {
} }
fn err_eof(&self, wanted: &str) -> Error { fn err_eof(&self, wanted: &str) -> Error {
self.err_syn(self.tokens.last().map(|tok| tok.span).unwrap_or((0, 0)), &format!("wanted {}, got eof", wanted)) self.err_syn(
self.tokens.last().map(|tok| tok.span).unwrap_or((0, 0)),
&format!("wanted {}, got eof", wanted),
)
} }
fn err_ty_unknown(&self, pos: (usize, usize), help: &str) -> Error { fn err_ty_unknown(&self, pos: (usize, usize), help: &str) -> Error {
@ -65,7 +61,7 @@ impl Parser {
result result
} }
pub fn next(&mut self) -> Result<Option<Statement>, Error> { pub fn parse_statement_option(&mut self) -> Result<Option<Statement>, Error> {
match self.peek_tok() { match self.peek_tok() {
Some(_) => Some(self.parse_statement()).transpose(), Some(_) => Some(self.parse_statement()).transpose(),
None => Ok(None), None => Ok(None),
@ -80,7 +76,7 @@ impl Parser {
TokenContent::Let => { TokenContent::Let => {
self.eat(TokenContent::Let)?; self.eat(TokenContent::Let)?;
let name = self.parse_ident()?; let name = self.parse_ident()?;
if let Ok(_) = self.eat(TokenContent::Symbol(Symbol::Colon)) { if self.eat(TokenContent::Symbol(Symbol::Colon)).is_ok() {
// TODO: types in variables // TODO: types in variables
self.parse_type()?; self.parse_type()?;
} }
@ -101,7 +97,10 @@ impl Parser {
let name = self.parse_ident()?; let name = self.parse_ident()?;
self.eat(TokenContent::OpenParan)?; self.eat(TokenContent::OpenParan)?;
let mut params = vec![]; let mut params = vec![];
while !self.peek_tok().is_some_and(|tok| tok.token == TokenContent::CloseParan) { while !self
.peek_tok()
.is_some_and(|tok| tok.token == TokenContent::CloseParan)
{
let name = self.parse_ident()?; let name = self.parse_ident()?;
self.eat(TokenContent::Symbol(Symbol::Colon))?; self.eat(TokenContent::Symbol(Symbol::Colon))?;
let ty = self.parse_type()?; let ty = self.parse_type()?;
@ -121,11 +120,20 @@ impl Parser {
self.eat(TokenContent::OpenBrace)?; self.eat(TokenContent::OpenBrace)?;
let block = self.parse_block()?; let block = self.parse_block()?;
self.eat(TokenContent::CloseBrace)?; self.eat(TokenContent::CloseBrace)?;
Statement::Func(Func { name, params, ret, block, public }) Statement::Func(Func {
name,
params,
ret,
block,
public,
})
} }
_ => { _ => {
let expr = self.parse_expr(0)?; let expr = self.parse_expr(0)?;
if self.peek_tok().is_some_and(|tk| tk.token == TokenContent::Symbol(Symbol::Semicolon)) { if self
.peek_tok()
.is_some_and(|tk| tk.token == TokenContent::Symbol(Symbol::Semicolon))
{
self.eat(TokenContent::Symbol(Symbol::Semicolon))?; self.eat(TokenContent::Symbol(Symbol::Semicolon))?;
Statement::Expr(expr) Statement::Expr(expr)
} else { } else {
@ -145,11 +153,19 @@ impl Parser {
"i32" => Type::Integer, "i32" => Type::Integer,
"f64" => Type::Float, "f64" => Type::Float,
"bool" => Type::Boolean, "bool" => Type::Boolean,
_ => return Err(self.err_ty_unknown(tok.span, "use a type that i know (currently only i32 and bool)")), _ => {
return Err(self.err_ty_unknown(
tok.span,
"use a type that i know (currently only i32 and bool)",
))
}
}, },
TokenContent::OpenParan => { TokenContent::OpenParan => {
let mut tys = vec![]; let mut tys = vec![];
while !self.peek_tok().is_some_and(|tok| tok.token == TokenContent::CloseParan) { while !self
.peek_tok()
.is_some_and(|tok| tok.token == TokenContent::CloseParan)
{
tys.push(self.parse_type()?); tys.push(self.parse_type()?);
if self.eat(TokenContent::Symbol(Symbol::Comma)).is_err() { if self.eat(TokenContent::Symbol(Symbol::Comma)).is_err() {
@ -158,7 +174,7 @@ impl Parser {
} }
self.eat(TokenContent::CloseParan)?; self.eat(TokenContent::CloseParan)?;
Type::Tuple(tys) Type::Tuple(tys)
}, }
_ => return Err(self.err_syn(tok.span, "this should be a pattern")), _ => return Err(self.err_syn(tok.span, "this should be a pattern")),
}; };
Ok(ty) Ok(ty)
@ -166,8 +182,13 @@ impl Parser {
fn parse_ident(&mut self) -> Result<String, Error> { fn parse_ident(&mut self) -> Result<String, Error> {
let result = match self.peek_tok() { let result = match self.peek_tok() {
Some(Token { token: TokenContent::Ident(ident), .. }) => Ok(ident.to_string()), Some(Token {
Some(t) => return Err(self.err_syn(t.span, &format!("expected ident, got {:?}", t.token))), token: TokenContent::Ident(ident),
..
}) => Ok(ident.to_string()),
Some(t) => {
return Err(self.err_syn(t.span, &format!("expected ident, got {:?}", t.token)))
}
None => return Err(self.err_eof("ident")), None => return Err(self.err_eof("ident")),
}; };
self.pos += 1; self.pos += 1;
@ -177,7 +198,10 @@ impl Parser {
fn parse_block(&mut self) -> Result<Block, Error> { fn parse_block(&mut self) -> Result<Block, Error> {
let mut statements = vec![]; let mut statements = vec![];
loop { loop {
if self.peek_tok().is_some_and(|tok| tok.token == TokenContent::CloseBrace) { if self
.peek_tok()
.is_some_and(|tok| tok.token == TokenContent::CloseBrace)
{
break; break;
} }
match self.parse_statement()? { match self.parse_statement()? {
@ -188,7 +212,10 @@ impl Parser {
stmt => statements.push(stmt), stmt => statements.push(stmt),
} }
match self.peek_tok() { match self.peek_tok() {
Some(Token { token: TokenContent::CloseBrace, .. }) => break, Some(Token {
token: TokenContent::CloseBrace,
..
}) => break,
_ => (), _ => (),
}; };
} }
@ -212,7 +239,10 @@ impl Parser {
let ident = ident.clone(); let ident = ident.clone();
if self.eat(TokenContent::OpenParan).is_ok() { if self.eat(TokenContent::OpenParan).is_ok() {
let mut params = vec![]; let mut params = vec![];
while !self.peek_tok().is_some_and(|tok| tok.token == TokenContent::CloseParan) { while !self
.peek_tok()
.is_some_and(|tok| tok.token == TokenContent::CloseParan)
{
params.push(self.parse_expr(0)?); params.push(self.parse_expr(0)?);
if self.eat(TokenContent::Symbol(Symbol::Comma)).is_err() { if self.eat(TokenContent::Symbol(Symbol::Comma)).is_err() {
break; break;
@ -233,17 +263,30 @@ impl Parser {
self.eat(TokenContent::OpenBrace)?; self.eat(TokenContent::OpenBrace)?;
let block = self.parse_block()?; let block = self.parse_block()?;
self.eat(TokenContent::CloseBrace)?; self.eat(TokenContent::CloseBrace)?;
let otherwise = if self.peek_tok().is_some_and(|t| t.token == TokenContent::Else) { let otherwise = if self
.peek_tok()
.is_some_and(|t| t.token == TokenContent::Else)
{
self.next_tok(); self.next_tok();
match self.peek_tok() { match self.peek_tok() {
Some(Token { token: TokenContent::OpenBrace, .. }) => { Some(Token {
token: TokenContent::OpenBrace,
..
}) => {
self.eat(TokenContent::OpenBrace)?; self.eat(TokenContent::OpenBrace)?;
let b = Some(self.parse_block()?); let b = Some(self.parse_block()?);
self.eat(TokenContent::CloseBrace)?; self.eat(TokenContent::CloseBrace)?;
b b
} }
Some(Token { token: TokenContent::If, .. }) => Some(Block(vec![Statement::TailExpr(self.parse_expr(0)?)])), Some(Token {
Some(tk) => return Err(self.err_syn(tk.span, "this should be followed by an if or block")), token: TokenContent::If,
..
}) => Some(Block(vec![Statement::TailExpr(self.parse_expr(0)?)])),
Some(tk) => {
return Err(
self.err_syn(tk.span, "this should be followed by an if or block")
)
}
None => return Err(self.err_eof("if or block")), None => return Err(self.err_eof("if or block")),
} }
} else { } else {
@ -260,7 +303,10 @@ impl Parser {
} }
TokenContent::Symbol(_) => { TokenContent::Symbol(_) => {
let Some(op) = PrefixOp::from_token(&tok.token) else { let Some(op) = PrefixOp::from_token(&tok.token) else {
return Err(self.err_syn(tok.span, "this should be changed into a valid operator or removed outright")); return Err(self.err_syn(
tok.span,
"this should be changed into a valid operator or removed outright",
));
}; };
let expr = self.parse_expr(1)?; let expr = self.parse_expr(1)?;
Expr::Unary(op, Box::new(expr)) Expr::Unary(op, Box::new(expr))
@ -279,7 +325,11 @@ impl Parser {
break; break;
} }
if self.peek_tok().is_none() || self.peek_tok().is_some_and(|t| t.token == TokenContent::CloseBrace) { if self.peek_tok().is_none()
|| self
.peek_tok()
.is_some_and(|t| t.token == TokenContent::CloseBrace)
{
break; break;
} }
} }

View file

@ -1,3 +1,3 @@
fn main() { fn main() {
let a = 10 * true; let a = 10 +
} }

View file

@ -1,14 +1,44 @@
use lang::{lexer::{Lexer, TokenContent, Symbol}, Error}; use lang::{
lexer::{Lexer, Symbol, Token, TokenContent},
Error,
};
#[test] #[test]
fn test_foo() { fn test_foo() {
let tokens: Result<Vec<_>, Error> = Lexer::new("1 * 5 / 3").collect(); let tokens: Result<Vec<_>, Error> = Lexer::new("1 * 5 / 3").collect();
let tokens = tokens.expect("should parse"); let tokens = tokens.expect("should parse");
assert_eq!(tokens, vec![ assert_eq!(
TokenContent::Number { radix: 10, text: "1".into() }, tokens,
TokenContent::Symbol(Symbol::Star), vec![
TokenContent::Number { radix: 10, text: "5".into() }, Token {
TokenContent::Symbol(Symbol::Slash), span: (0, 1),
TokenContent::Number { radix: 10, text: "3".into() }, token: TokenContent::Number {
]) radix: 10,
text: "1".into()
},
},
Token {
span: (1, 1),
token: TokenContent::Symbol(Symbol::Star),
},
Token {
span: (2, 1),
token: TokenContent::Number {
radix: 10,
text: "5".into()
},
},
Token {
span: (3, 1),
token: TokenContent::Symbol(Symbol::Slash),
},
Token {
span: (4, 1),
token: TokenContent::Number {
radix: 10,
text: "3".into()
},
},
]
)
} }