diff --git a/blank.html b/blank.html new file mode 100644 index 0000000..d17ee78 --- /dev/null +++ b/blank.html @@ -0,0 +1,2 @@ + + diff --git a/src/data.rs b/src/data.rs index 567eaa8..a900289 100644 --- a/src/data.rs +++ b/src/data.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +// TODO: TypedStatement and TypedExpression? use crate::lexer::{Token, Symbol}; @@ -18,7 +18,6 @@ pub enum BinaryOp { LogicOr, // TODO // Set, - Comma, } #[derive(Debug, Clone)] @@ -50,7 +49,8 @@ pub struct Func { pub name: String, pub params: Vec<(String, Type)>, pub ret: Type, - pub block: Block + pub block: Block, + pub public: bool, } #[derive(Debug, Clone)] @@ -109,7 +109,7 @@ impl BinaryOp { Self::BitOr => (6, 7), Self::LogicAnd => (4, 5), Self::LogicOr => (2, 3), - Self::Comma => (0, 1), + // Self::Comma => (0, 1), } } @@ -169,10 +169,10 @@ impl Type { pub fn string(&self) -> String { match self { Self::Integer => "i32".into(), - Self::Float => "f32".into(), + Self::Float => "f64".into(), Self::Boolean => "i32".into(), Self::Tuple(v) => { - let s: Vec<_> = v.into_iter().map(Type::string).collect(); + let s: Vec<_> = v.iter().map(Type::string).collect(); format!("({})", s.join(", ")) }, _ => unimplemented!(), diff --git a/src/error.rs b/src/error.rs index 2cb8549..22c918f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,6 +3,7 @@ pub enum Error { SyntaxError(String), TypeError(String), ReferenceError(String), + IoError(std::io::Error), } impl Error { @@ -14,3 +15,9 @@ impl Error { Error::TypeError(what.to_string()) } } + +impl From for Error { + fn from(value: std::io::Error) -> Self { + Error::IoError(value) + } +} diff --git a/src/generator.rs b/src/generator.rs index 7368161..e644c05 100644 --- a/src/generator.rs +++ b/src/generator.rs @@ -1,11 +1,13 @@ /* optimizations -- use i32.eqz when comparing to zero -- write negative numberss directly instead of as positive + sign flip +- [ ] use i32.eqz when comparing to zero +- [x] write negative numbers directly instead of as positive + sign flip +- [ ] replace `local.set` + `local.get` with `local.tee` +- [ ] don't create local variables at all if they're only used once */ -use crate::data::{self, BinaryOp, Block, Expr, Func, Literal, Pattern, PrefixOp, Statement, Type}; +use crate::data::{BinaryOp, Expr, Func, Literal, Pattern, PrefixOp, Statement, Type}; use crate::parser::Context; use crate::Error; @@ -13,57 +15,86 @@ pub struct Generator { output: Box, } +struct Allocator { + count: u32, +} + +// const PRELUDE: &str = r#" +// ;; begin prelude +// (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32))) +// (export "memory" (memory 0)) +// ;; (func $print (param $offset i32) (param $length i32) +// (func $print (param $string i32) +// ;; (i32.store (i32.const 0) (local.get $offset)) ;; iov.iov_base (pointer to string) +// ;; (i32.store (i32.const 4) (local.get $length)) ;; iov.iov_len (length of string) +// ;; (call $fd_write +// ;; (i32.const 1) ;; fd: stdout = 1 +// ;; (i32.const 0) ;; data: pointer to memory - this is the first memory we create (index 0) +// ;; (i32.const 1) ;; data_len: there's 1 string +// ;; (i32.const 0) ;; nwritten: i don't care about this, write it wherever +// ;; ) +// ;; drop +// ) +// ;; end prelude +// "#; + impl Generator { pub fn new(output: Box) -> Generator { Generator { output } } - fn write_module(&mut self, stmts: &[Statement]) -> Result<(), Error> { + pub fn write_module(&mut self, stmts: &[Statement]) -> Result<(), Error> { + writeln!(self.output, "(module")?; let mut ctx = Context::new(); - write!(self.output, "(module"); + // ctx.funcs.insert("print".to_string(), (vec![("message".to_string(), Type::String)], Type::empty())); + + // writeln!(self.output, "{}", PRELUDE)?; + + let mut gctx = GenContext::new(); + let mut alloc = Allocator::new(); + 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); + // if !gctx.strings.is_empty() { + // writeln!(self.output, "(memory 1)")?; + // for (s, offset) in &mut gctx.strings { + // writeln!(self.output, "(data $strings (i32.const {}) \"{}\")", offset, s.replace("\n", "\\n").replace("\"", "\\\""))?; + // } + // } + for stmt in stmts { match stmt { - Statement::Func(Func { name, ret, params, .. }) => { - ctx.funcs.insert(name.clone(), (params.clone(), ret.clone())); - } + Statement::Func(func) => self.write_func(&gctx, &ctx, func)?, Statement::Let(..) | Statement::TailExpr(..) | Statement::Expr (..) => { return Err(Error::syn("incorrect top level statement")) } }; } - for stmt in stmts { - match stmt { - Statement::Func(func) => self.write_func(&ctx, func)?, - _ => { - return Err(Error::syn("incorrect top level statement")) - } - }; - } - write!(self.output, ")"); + writeln!(self.output, ")")?; Ok(()) } - fn write_func(&mut self, ctx: &Context, func: &Func) -> Result<(), Error> { - write!(self.output, " (func ${}", func.name); + fn write_func(&mut self, parent_gctx: &GenContext, ctx: &Context, func: &Func) -> Result<(), Error> { + write!(self.output, "(func ${}", func.name)?; if func.name == "main" { - write!(self.output, " (export \"_start\")"); + write!(self.output, " (export \"_start\")")?; + } else if func.public { + write!(self.output, " (export \"{}\")", func.name)?; } let expr = Expr::Block(func.block.clone()); let mut ctx = ctx.clone(); - let mut exprs = Vec::new(); for (name, ty) in &func.params { ctx.locals.insert(name.clone(), ty.clone()); - write!(self.output, " (param ${} {})", name, ty.string()); + write!(self.output, " (param ${} {})", name, ty.string())?; } if !func.ret.is_empty() { - write!(self.output, " (result {})", func.ret.string()); + write!(self.output, " (result {})", func.ret.string())?; } - writeln!(self.output); - writeln!(self.output, "(local $match i32)"); + writeln!(self.output)?; + writeln!(self.output, "(local $match i32)")?; let inferred = func.block.infer(&ctx)?; if func.ret != inferred { @@ -73,170 +104,263 @@ impl Generator { ))); } - get_locals(&expr, &mut ctx, &mut exprs); + let mut alloc = Allocator::new(); + let mut gctx = GenContext::new(); + get_locals(&expr, &mut alloc, &mut ctx, &mut gctx); + let exprs = gctx.exprs; - for (name, _) in &exprs { - let ty = match ctx.locals.get(name).unwrap() { + for (name, expr) in &exprs { + let ty = match expr.infer(&ctx).unwrap() { Type::Integer => "i32", Type::Float => "f64", + // Type::String => "i32", _ => todo!(), }; - println!("(local ${name} {ty})"); + writeln!(self.output, "(local ${name} {ty})")?; } for (name, expr) in &exprs { - gen_expr(expr, &ctx); - println!("local.set ${name}"); + self.gen_expr(expr, parent_gctx, &ctx)?; + writeln!(self.output, "local.set ${name}")?; } - gen_expr(&expr, &ctx); + self.gen_expr(&expr, parent_gctx, &ctx)?; - write!(self.output, ")"); + writeln!(self.output, ")")?; Ok(()) } + + fn gen_expr(&mut self, expr: &Expr, parent_gctx: &GenContext, ctx: &Context) -> Result<(), Error> { + match expr { + Expr::Literal(lit) => match lit { + Literal::Integer(int) => writeln!(self.output, "i32.const {int}")?, + Literal::Float(f) => writeln!(self.output, "f64.const {f}")?, + 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())?, + _ => todo!(), + }, + Expr::Variable(name) => { + writeln!(self.output, "local.get ${name}")?; + } + Expr::Binary(op, a, b) => { + self.gen_expr(a, parent_gctx, ctx)?; + self.gen_expr(b, parent_gctx, ctx)?; + + let ty = match expr.infer(ctx).unwrap() { + Type::Integer => "i32", + Type::Float => "f64", + Type::Boolean => "i32", + _ => todo!(), + }; + match op { + BinaryOp::Add => writeln!(self.output, "{ty}.add")?, + BinaryOp::Mul => writeln!(self.output, "{ty}.mul")?, + BinaryOp::Sub => writeln!(self.output, "{ty}.sub")?, + BinaryOp::Div => writeln!(self.output, "{ty}.div_u")?, // do i _u or _s? + BinaryOp::Mod => writeln!(self.output, "{ty}.rem_u")?, + BinaryOp::Eq => writeln!(self.output, "{ty}.eq")?, + BinaryOp::Neq => writeln!(self.output, "{ty}.neq")?, + BinaryOp::Less => writeln!(self.output, "{ty}.lt_u")?, + BinaryOp::Greater => writeln!(self.output, "{ty}.gt_u")?, + _ => todo!(), + } + } + Expr::Unary(op, e) => { + if let Expr::Literal(lit) = e.as_ref() { + match lit { + Literal::Integer(int) => writeln!(self.output, "i32.const {}", -int)?, + Literal::Float(f) => writeln!(self.output, "f64.const {}", -f)?, + _ => todo!(), + } + return Ok(()); + } + + self.gen_expr(e, parent_gctx, ctx)?; + match op { + PrefixOp::Minus => { + // this is so inefficent, but i don't care + writeln!(self.output, "i32.const -1")?; + writeln!(self.output, "i32.mul")?; + } + PrefixOp::LogicNot => { + writeln!(self.output, "i32.eqz")?; + } + PrefixOp::BitNot => { + // TODO: do i flip the sign bit? + writeln!(self.output, "i32.const {}", i32::MAX)?; + writeln!(self.output, "i32.xor")?; + } + } + } + // FIXME: awful code until i fix patching up parser and lexer + Expr::Match(cond, arms) => { + self.gen_expr(cond, parent_gctx, ctx)?; + writeln!(self.output, "local.set $match")?; + + for (idx, (pat, expr)) in arms.iter().enumerate() { + match pat { + Pattern::Literal(lit) => match lit { + Literal::Integer(int) => writeln!(self.output, "i32.const {}", int)?, + Literal::Boolean(b) => writeln!(self.output, "i32.const {}", if *b { 1 } else { 0 })?, + _ => todo!(), + }, + }; + + writeln!(self.output, "local.get $match")?; + writeln!(self.output, "i32.eq")?; + writeln!(self.output, "(if (result i32) (then")?; + self.gen_expr(expr, parent_gctx, ctx)?; + + if idx == arms.len() - 1 { + // TODO: verify its actually unreachable earlier on + writeln!(self.output, ") (else unreachable")?; + } else { + writeln!(self.output, ") (else")?; + } + } + writeln!(self.output, "{}", ")".repeat(arms.len() * 2))?; + } + Expr::Block(b) => { + let mut ctx = ctx.clone(); + for (_i, stmt) in b.0.iter().enumerate() { + match stmt { + Statement::TailExpr(expr) => { + self.gen_expr(expr, parent_gctx, &ctx)?; + } + Statement::Expr(expr) => { + self.gen_expr(expr, parent_gctx, &ctx)?; + if !expr.infer(&ctx).unwrap().is_empty() { + writeln!(self.output, "drop")?; + } + } + Statement::Let(name, expr) => { + let ty = expr.infer(&ctx).unwrap(); + ctx.locals.insert(name.clone(), ty); + // exprs.push((name.clone(), expr.clone())); + } + _ => {} + } + } + } + Expr::Call(func, args) => { + expr.infer(ctx)?; + for expr in args { + self.gen_expr(expr, parent_gctx, ctx)?; + } + writeln!(self.output, "call ${func}")?; + } + }; + Ok(()) + } } -pub fn generate(statements: &[Statement]) -> Result<(), Error> { - let mut gen = Generator::new(Box::new(std::io::stdout())); - gen.write_module(statements) +// FIXME: not sure what i was thinking +impl Allocator { + fn new() -> Allocator { + Allocator { + count: 0, + } + } + + fn alloc_ident(&mut self, name: &str) -> String { + // self.count += 1; + // format!("{name}_{}", self.count) + name.to_string() + } } -fn gen_expr(expr: &Expr, ctx: &Context) { - match expr { - Expr::Literal(lit) => match lit { - Literal::Integer(int) => println!("i32.const {int}"), - Literal::Float(f) => println!("f64.const {f}"), - Literal::Boolean(b) => println!("i32.const {}", if *b { 1 } else { 0 }), - _ => todo!(), - }, - Expr::Variable(name) => { - println!("local.get ${name}"); - } - Expr::Binary(op, a, b) => { - gen_expr(&a, ctx); - gen_expr(&b, ctx); - - let ty = match expr.infer(&ctx).unwrap() { - Type::Integer => "i32", - Type::Float => "f64", - Type::Boolean => "i32", - _ => todo!(), - }; - match op { - BinaryOp::Add => println!("{ty}.add"), - BinaryOp::Mul => println!("{ty}.mul"), - BinaryOp::Sub => println!("{ty}.sub"), - BinaryOp::Div => println!("{ty}.div_u"), // do i _u or _s? - BinaryOp::Mod => println!("{ty}.rem_u"), - BinaryOp::Eq => println!("{ty}.eq"), - BinaryOp::Neq => println!("{ty}.neq"), - BinaryOp::Less => println!("{ty}.lt_u"), - BinaryOp::Greater => println!("{ty}.gt_u"), - _ => todo!(), - } - } - Expr::Unary(op, e) => { - if let Expr::Literal(lit) = e.as_ref() { - match lit { - Literal::Integer(int) => println!("i32.const {}", -int), - Literal::Float(f) => println!("f64.const {}", -f), - _ => unreachable!(), - } - return; - } - - gen_expr(e, ctx); - match op { - PrefixOp::Minus => { - // this is so inefficent, but i don't care - println!("i32.const -1"); - println!("i32.mul"); - } - PrefixOp::LogicNot => { - println!("i32.eqz"); - } - PrefixOp::BitNot => { - // TODO: do i flip the sign bit? - println!("i32.const {}", i32::MAX); - println!("i32.xor"); - } - } - } - // FIXME: awful code until i fix patching up parser and lexer - Expr::Match(cond, arms) => { - gen_expr(cond, ctx); - println!("local.set $match"); - - for (idx, (pat, expr)) in arms.iter().enumerate() { - match pat { - Pattern::Literal(lit) => match lit { - Literal::Integer(int) => println!("i32.const {}", int), - Literal::Boolean(b) => println!("i32.const {}", if *b { 1 } else { 0 }), - _ => todo!(), - }, - }; - - println!("local.get $match"); - println!("i32.eq"); - println!("(if (result i32) (then"); - gen_expr(expr, ctx); - - if idx == arms.len() - 1 { - // TODO: verify its actually unreachable earlier on - println!(") (else unreachable"); - } else { - println!(") (else"); - } - } - println!("{}", ")".repeat(arms.len() * 2)); - } - Expr::Block(b) => { - for (i, stmt) in b.0.iter().enumerate() { - match stmt { - Statement::TailExpr(expr) => { - gen_expr(expr, &ctx); - } - Statement::Expr(expr) => { - gen_expr(expr, &ctx); - println!("drop"); - } - _ => {} - } - } - } - Expr::Call(func, args) => { - for expr in args { - gen_expr(expr, ctx); - } - println!("call ${func}"); - } - }; +#[derive(Debug)] +struct GenContext { + exprs: Vec<(String, Expr)>, + strings: Vec<(String, usize)>, } -// FIXME: block scoped variables -fn get_locals(expr: &Expr, ctx: &mut Context, exprs: &mut Vec<(String, Expr)>) { +impl GenContext { + pub fn new() -> GenContext { + GenContext { + exprs: vec![], + strings: vec![], + } + } +} + +fn get_locals(expr: &Expr, alloc: &mut Allocator, ctx: &mut Context, gctx: &mut GenContext) { match expr { Expr::Block(b) => { for stmt in &b.0 { match stmt { Statement::Let(name, expr) => { + // FIXME: block scoped variables (name collisions) + get_locals(expr, alloc, ctx, gctx); + let ty = expr.infer(ctx).unwrap(); ctx.locals.insert(name.clone(), ty); - exprs.push((name.clone(), expr.clone())); + gctx.exprs.push((alloc.alloc_ident(name), expr.clone())); } - Statement::TailExpr(expr) => get_locals(&expr, ctx, exprs), - Statement::Expr(expr) => get_locals(&expr, ctx, exprs), - Statement::Func(Func { name, ret, .. }) => { - ctx.locals.insert(name.clone(), todo!()); + Statement::TailExpr(expr) => get_locals(expr, alloc, ctx, gctx), + Statement::Expr(expr) => get_locals(expr, alloc, ctx, gctx), + Statement::Func(Func { name, ret, params, .. }) => { + ctx.funcs.insert(name.clone(), (params.clone(), ret.clone())); } } } } - Expr::Unary(_, expr) => get_locals(&expr, ctx, exprs), + Expr::Unary(_, expr) => get_locals(expr, alloc, ctx, gctx), Expr::Binary(_, a, b) => { - get_locals(&a, ctx, exprs); - get_locals(&b, ctx, exprs); + get_locals(a, alloc, ctx, gctx); + get_locals(b, alloc, ctx, gctx); } - _ => (), + Expr::Literal(Literal::String(s)) => { + let offset = gctx.strings.last().map(|(s, off)| off + s.len()).unwrap_or(8); + gctx.strings.push((s.clone(), offset)); + } + Expr::Match(pat, arms) => { + get_locals(pat, alloc, ctx, gctx); + for (_, arm) in arms { + get_locals(arm, alloc, ctx, gctx); + } + } + Expr::Call(_, exprs) => { + for expr in exprs { + get_locals(expr, alloc, ctx, gctx); + } + } + Expr::Variable(..) | Expr::Literal(..) => (), } } + +// fn get_strings(expr: &Expr, gctx: &mut GenContext) { +// match expr { +// Expr::Block(b) => { +// for stmt in &b.0 { +// match stmt { +// Statement::Let(_, expr) => get_strings(expr, gctx), +// Statement::TailExpr(expr) => get_strings(expr, gctx), +// Statement::Expr(expr) => get_strings(expr, gctx), +// Statement::Func(Func { block, .. }) => get_strings(&Expr::Block(block.clone()), gctx), +// } +// } +// } +// Expr::Unary(_, expr) => get_strings(expr, gctx), +// Expr::Binary(_, a, b) => { +// get_strings(a, gctx); +// get_strings(b, gctx); +// } +// Expr::Literal(Literal::String(s)) => { +// let offset = gctx.strings.last().map(|(s, off)| off + s.len()).unwrap_or(8); +// gctx.strings.push((s.clone(), offset)); +// } +// Expr::Match(pat, arms) => { +// get_strings(pat, gctx); +// for (_, arm) in arms { +// get_strings(arm, gctx); +// } +// } +// Expr::Call(_, exprs) => { +// for expr in exprs { +// get_strings(expr, gctx); +// } +// } +// Expr::Variable(..) | Expr::Literal(..) => (), +// } +// } diff --git a/src/lexer.rs b/src/lexer.rs index 6ac76de..9949537 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -24,6 +24,7 @@ pub enum Token { True, False, If, Else, Match, While, Loop, For, Break, Continue, + Pub, } #[rustfmt::skip] @@ -89,6 +90,7 @@ impl Lexer { "for" => Token::For, "break" => Token::Break, "continue" => Token::Continue, + "pub" => Token::Pub, ident => Token::Ident(ident.to_string()), }, ch if ch.is_whitespace() => { @@ -103,7 +105,7 @@ impl Lexer { fn lex_number(&mut self) -> Result { let mut buffer = String::new(); let radix = match (self.input[self.pos], self.input.get(self.pos + 1)) { - ('0', Some(ch)) if ch.is_digit(10) => 10, + ('0', Some(ch)) if ch.is_ascii_digit() => 10, ('0', Some(ch)) => { self.pos += 2; match ch { diff --git a/src/main.rs b/src/main.rs index f4eab72..bf39815 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ a second time when generating (so the types are known), there should be a better way */ -#![allow(unused)] +#![allow(dead_code, clippy::single_match, clippy::only_used_in_recursion)] mod data; mod error; @@ -14,19 +14,7 @@ mod parser; mod types; pub use error::Error; -use parser::Context; - -use crate::data::Statement; - -pub struct Foo { - a: u8, - b: Bar, -} - -pub struct Bar { - a: u8, - b: i32, -} +use generator::Generator; fn main() { // let mut lexer = lexer::Lexer::new(" @@ -54,21 +42,44 @@ fn main() { // ".into()); let mut lexer = lexer::Lexer::new(" fn main() -> i32 { - if is_three(add_four(-1)) { + let n = -1; + if is_three(add_four(n)) { 20 } else { 30 } } - fn add_four(n: i32) -> i32 { + pub fn add_four(n: i32) -> i32 { n + 4 } fn is_three(n: i32) -> bool { n == 3 } + + fn add_four_float(n: f64) -> f64 { + n + 4.0 + } ".into()); + // let mut lexer = lexer::Lexer::new(" + // fn main() -> i32 { + // let x = { + // let y = 20; + // y + 10 + // }; + // x + // } + // ".into()); + // let mut lexer = lexer::Lexer::new(r#" + // fn main() { + // let x = "example string!\n"; + // let y = "another str.\n"; + // print(x); + // print(y); + // print("Hello, world!"); + // } + // "#.into()); let mut tokens = vec![]; loop { @@ -89,17 +100,7 @@ fn main() { loop { match parser.next() { Ok(None) => break, - Ok(Some(tree)) => { - // dbg!(&tree); - // match &tree { - // Statement::Expr(expr) => match expr.infer(&Context::new()) { - // Ok(ty) => eprintln!("type: {:?}", ty), - // Err(err) => eprintln!("err: {:?}", err), - // }, - // _ => todo!(), - // }; - statements.push(tree); - } + Ok(Some(tree)) => statements.push(tree), Err(error) => { eprintln!("error: {:?}", error); return; @@ -107,7 +108,11 @@ fn main() { } } - if let Err(err) = generator::generate(&statements) { + // dbg!(&statements); + + let mut gen = Generator::new(Box::new(std::io::stdout())); + + if let Err(err) = gen.write_module(&statements) { panic!("{:?}", err); } } diff --git a/src/parser.rs b/src/parser.rs index 3aaf8c3..b39fd64 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -47,7 +47,7 @@ impl Parser { fn parse_statement(&mut self) -> Result, Error> { let Some(tok) = self.peek_tok() else { - return Err(Error::SyntaxError(format!("unexpected eof"))); + return Err(Error::syn("unexpected eof")); }; let stmt = match tok { Token::Let => { @@ -58,7 +58,14 @@ impl Parser { self.eat(Token::Symbol(Symbol::Semicolon))?; Statement::Let(name, expr) } - Token::Fn => { + Token::Pub | Token::Fn => { + // TODO: public things that aren't functions + let public = if tok == &Token::Pub { + self.eat(Token::Pub)?; + true + } else { + false + }; self.eat(Token::Fn)?; let name = self.parse_ident()?; self.eat(Token::OpenParan)?; @@ -86,7 +93,7 @@ impl Parser { self.eat(Token::OpenBrace)?; let block = self.parse_block()?; self.eat(Token::CloseBrace)?; - Statement::Func(Func { name, params, ret, block }) + Statement::Func(Func { name, params, ret, block, public }) } _ => { let expr = self.parse_expr(0)?; @@ -103,29 +110,29 @@ impl Parser { fn parse_type(&mut self) -> Result { let Some(tok) = self.next_tok() else { - return Err(Error::SyntaxError(format!("unexpected eof"))); + return Err(Error::syn("unexpected eof")); }; - match tok { + let ty = match tok { Token::Ident(ident) => match ident.as_str() { - "i32" => Ok(Type::Integer), - "bool" => Ok(Type::Boolean), + "i32" => Type::Integer, + "f64" => Type::Float, + "bool" => Type::Boolean, _ => return Err(Error::TypeError(format!("unknown type {ident}"))), }, _ => todo!(), - } + }; + Ok(ty) } fn parse_ident(&mut self) -> Result { match self.next_tok() { Some(Token::Ident(ident)) => Ok(ident.to_string()), Some(tk) => { - return Err(Error::SyntaxError(format!( + Err(Error::SyntaxError(format!( "expected identifier, got {tk:?}" ))) } - None => { - return Err(Error::SyntaxError(format!("expected identifier, got eof"))) - } + None => Err(Error::syn("expected identifier, got eof")), } } @@ -142,7 +149,7 @@ impl Parser { statements.push(stmt); match self.peek_tok() { Some(Token::CloseBrace) => break, - tok => return Err(Error::SyntaxError(format!("unexpected token {tok:?}"))), + Some(tok) => return Err(Error::SyntaxError(format!("unexpected token {tok:?}"))), None => return Err(Error::syn("unexpected eof")), }; } @@ -153,7 +160,6 @@ impl Parser { // Some(_) => return Err(Error::SyntaxError(format!("unexpected token {tok:?}"))), // tok => return Err(Error::SyntaxError(format!("unexpected token {tok:?}"))), _ => (), - None => return Err(Error::syn("unexpected eof")), }; } Ok(Block(statements)) @@ -172,7 +178,7 @@ impl Parser { Token::Ident(ident) => { let ident = ident.clone(); if self.peek_tok().is_some_and(|t| *t == Token::OpenParan) { - self.eat(Token::OpenParan); + self.eat(Token::OpenParan)?; let mut params = vec![]; while !self.peek_tok().is_some_and(|tok| tok == &Token::CloseParan) { params.push(self.parse_expr(0)?); @@ -182,7 +188,7 @@ impl Parser { break; } } - self.eat(Token::CloseParan); + self.eat(Token::CloseParan)?; Expr::Call(ident, params) } else { Expr::Variable(ident) @@ -190,6 +196,7 @@ impl Parser { } Token::False => Expr::Literal(Literal::Boolean(false)), Token::True => Expr::Literal(Literal::Boolean(true)), + Token::String(s) => Expr::Literal(Literal::String(s.to_string())), Token::Char(ch) => Expr::Literal(Literal::Char(*ch)), Token::If => { let cond = self.parse_expr(0)?; @@ -222,7 +229,7 @@ impl Parser { Expr::Match(Box::new(cond), map) } Token::Symbol(_) => { - let Some(op) = PrefixOp::from_token(&tok) else { + let Some(op) = PrefixOp::from_token(tok) else { return Err(Error::SyntaxError(format!("unexpected token {tok:?}"))); }; let expr = self.parse_expr(1)?; diff --git a/test.wasm b/test.wasm new file mode 100644 index 0000000..3d60e4d Binary files /dev/null and b/test.wasm differ diff --git a/test.wat b/test.wat index 7567f30..8b750ba 100644 --- a/test.wat +++ b/test.wat @@ -1 +1,43 @@ -(module (func $name (export "_start") (result i32) i32.const 10)) +(module +(func $main (export "_start") (result i32) +(local $match i32) +(local $n i32) +i32.const -1 +local.set $n +local.get $n +call $add_four +call $is_three +local.set $match +i32.const 1 +local.get $match +i32.eq +(if (result i32) (then +i32.const 20 +) (else +i32.const 0 +local.get $match +i32.eq +(if (result i32) (then +i32.const 30 +) (else unreachable +)))) +) +(func $add_four (export "add_four") (param $n i32) (result i32) +(local $match i32) +local.get $n +i32.const 4 +i32.add +) +(func $is_three (param $n i32) (result i32) +(local $match i32) +local.get $n +i32.const 3 +i32.eq +) +(func $add_four_float (param $n f64) (result f64) +(local $match i32) +local.get $n +f64.const 4 +f64.add +) +)