This commit is contained in:
tezlm 2023-10-02 18:27:33 -07:00
parent 06b97d3132
commit 0d81019cb1
Signed by: tezlm
GPG key ID: 649733FCD94AFBBA
9 changed files with 399 additions and 210 deletions

2
blank.html Normal file
View file

@ -0,0 +1,2 @@
<!DOCTYPE html>
<!-- blank html file + miniserve because of cors -->

View file

@ -1,4 +1,4 @@
use std::collections::HashMap; // TODO: TypedStatement and TypedExpression?
use crate::lexer::{Token, Symbol}; use crate::lexer::{Token, Symbol};
@ -18,7 +18,6 @@ pub enum BinaryOp {
LogicOr, LogicOr,
// TODO // TODO
// Set, // Set,
Comma,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -50,7 +49,8 @@ pub struct Func {
pub name: String, pub name: String,
pub params: Vec<(String, Type)>, pub params: Vec<(String, Type)>,
pub ret: Type, pub ret: Type,
pub block: Block pub block: Block,
pub public: bool,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -109,7 +109,7 @@ impl BinaryOp {
Self::BitOr => (6, 7), Self::BitOr => (6, 7),
Self::LogicAnd => (4, 5), Self::LogicAnd => (4, 5),
Self::LogicOr => (2, 3), Self::LogicOr => (2, 3),
Self::Comma => (0, 1), // Self::Comma => (0, 1),
} }
} }
@ -169,10 +169,10 @@ impl Type {
pub fn string(&self) -> String { pub fn string(&self) -> String {
match self { match self {
Self::Integer => "i32".into(), Self::Integer => "i32".into(),
Self::Float => "f32".into(), Self::Float => "f64".into(),
Self::Boolean => "i32".into(), Self::Boolean => "i32".into(),
Self::Tuple(v) => { 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(", ")) format!("({})", s.join(", "))
}, },
_ => unimplemented!(), _ => unimplemented!(),

View file

@ -3,6 +3,7 @@ pub enum Error {
SyntaxError(String), SyntaxError(String),
TypeError(String), TypeError(String),
ReferenceError(String), ReferenceError(String),
IoError(std::io::Error),
} }
impl Error { impl Error {
@ -14,3 +15,9 @@ impl Error {
Error::TypeError(what.to_string()) Error::TypeError(what.to_string())
} }
} }
impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
Error::IoError(value)
}
}

View file

@ -1,11 +1,13 @@
/* /*
optimizations optimizations
- use i32.eqz when comparing to zero - [ ] use i32.eqz when comparing to zero
- write negative numberss directly instead of as positive + sign flip - [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::parser::Context;
use crate::Error; use crate::Error;
@ -13,57 +15,86 @@ pub struct Generator {
output: Box<dyn std::io::Write>, output: Box<dyn std::io::Write>,
} }
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 { impl Generator {
pub fn new(output: Box<dyn std::io::Write>) -> Generator { pub fn new(output: Box<dyn std::io::Write>) -> Generator {
Generator { output } 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(); 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 { for stmt in stmts {
match stmt { match stmt {
Statement::Func(Func { name, ret, params, .. }) => { Statement::Func(func) => self.write_func(&gctx, &ctx, func)?,
ctx.funcs.insert(name.clone(), (params.clone(), ret.clone()));
}
Statement::Let(..) | Statement::TailExpr(..) | Statement::Expr (..) => { Statement::Let(..) | Statement::TailExpr(..) | Statement::Expr (..) => {
return Err(Error::syn("incorrect top level statement")) return Err(Error::syn("incorrect top level statement"))
} }
}; };
} }
for stmt in stmts { writeln!(self.output, ")")?;
match stmt {
Statement::Func(func) => self.write_func(&ctx, func)?,
_ => {
return Err(Error::syn("incorrect top level statement"))
}
};
}
write!(self.output, ")");
Ok(()) Ok(())
} }
fn write_func(&mut self, 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" {
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 expr = Expr::Block(func.block.clone());
let mut ctx = ctx.clone(); let mut ctx = ctx.clone();
let mut exprs = Vec::new();
for (name, ty) in &func.params { for (name, ty) in &func.params {
ctx.locals.insert(name.clone(), ty.clone()); 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() { 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)?;
writeln!(self.output, "(local $match i32)"); writeln!(self.output, "(local $match i32)")?;
let inferred = func.block.infer(&ctx)?; let inferred = func.block.infer(&ctx)?;
if func.ret != inferred { 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 { for (name, expr) in &exprs {
let ty = match ctx.locals.get(name).unwrap() { let ty = match expr.infer(&ctx).unwrap() {
Type::Integer => "i32", Type::Integer => "i32",
Type::Float => "f64", Type::Float => "f64",
// Type::String => "i32",
_ => todo!(), _ => todo!(),
}; };
println!("(local ${name} {ty})"); writeln!(self.output, "(local ${name} {ty})")?;
} }
for (name, expr) in &exprs { for (name, expr) in &exprs {
gen_expr(expr, &ctx); self.gen_expr(expr, parent_gctx, &ctx)?;
println!("local.set ${name}"); writeln!(self.output, "local.set ${name}")?;
} }
gen_expr(&expr, &ctx); self.gen_expr(&expr, parent_gctx, &ctx)?;
write!(self.output, ")"); writeln!(self.output, ")")?;
Ok(()) 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> { // FIXME: not sure what i was thinking
let mut gen = Generator::new(Box::new(std::io::stdout())); impl Allocator {
gen.write_module(statements) 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) { #[derive(Debug)]
match expr { struct GenContext {
Expr::Literal(lit) => match lit { exprs: Vec<(String, Expr)>,
Literal::Integer(int) => println!("i32.const {int}"), strings: Vec<(String, usize)>,
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}");
}
};
} }
// FIXME: block scoped variables impl GenContext {
fn get_locals(expr: &Expr, ctx: &mut Context, exprs: &mut Vec<(String, Expr)>) { 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 { match expr {
Expr::Block(b) => { Expr::Block(b) => {
for stmt in &b.0 { for stmt in &b.0 {
match stmt { match stmt {
Statement::Let(name, expr) => { Statement::Let(name, expr) => {
// FIXME: block scoped variables (name collisions)
get_locals(expr, alloc, ctx, gctx);
let ty = expr.infer(ctx).unwrap(); let ty = expr.infer(ctx).unwrap();
ctx.locals.insert(name.clone(), ty); 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::TailExpr(expr) => get_locals(expr, alloc, ctx, gctx),
Statement::Expr(expr) => get_locals(&expr, ctx, exprs), Statement::Expr(expr) => get_locals(expr, alloc, ctx, gctx),
Statement::Func(Func { name, ret, .. }) => { Statement::Func(Func { name, ret, params, .. }) => {
ctx.locals.insert(name.clone(), todo!()); 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) => { Expr::Binary(_, a, b) => {
get_locals(&a, ctx, exprs); get_locals(a, alloc, ctx, gctx);
get_locals(&b, ctx, exprs); 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(..) => (),
// }
// }

View file

@ -24,6 +24,7 @@ pub enum Token {
True, False, True, False,
If, Else, Match, If, Else, Match,
While, Loop, For, Break, Continue, While, Loop, For, Break, Continue,
Pub,
} }
#[rustfmt::skip] #[rustfmt::skip]
@ -89,6 +90,7 @@ impl Lexer {
"for" => Token::For, "for" => Token::For,
"break" => Token::Break, "break" => Token::Break,
"continue" => Token::Continue, "continue" => Token::Continue,
"pub" => Token::Pub,
ident => Token::Ident(ident.to_string()), ident => Token::Ident(ident.to_string()),
}, },
ch if ch.is_whitespace() => { ch if ch.is_whitespace() => {
@ -103,7 +105,7 @@ impl Lexer {
fn lex_number(&mut self) -> Result<Token, Error> { fn lex_number(&mut self) -> Result<Token, Error> {
let mut buffer = String::new(); let mut buffer = String::new();
let radix = match (self.input[self.pos], self.input.get(self.pos + 1)) { 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)) => { ('0', Some(ch)) => {
self.pos += 2; self.pos += 2;
match ch { match ch {

View file

@ -4,7 +4,7 @@ a second time when generating (so the types are known), there should be
a better way a better way
*/ */
#![allow(unused)] #![allow(dead_code, clippy::single_match, clippy::only_used_in_recursion)]
mod data; mod data;
mod error; mod error;
@ -14,19 +14,7 @@ mod parser;
mod types; mod types;
pub use error::Error; pub use error::Error;
use parser::Context; use generator::Generator;
use crate::data::Statement;
pub struct Foo {
a: u8,
b: Bar,
}
pub struct Bar {
a: u8,
b: i32,
}
fn main() { fn main() {
// let mut lexer = lexer::Lexer::new(" // let mut lexer = lexer::Lexer::new("
@ -54,21 +42,44 @@ fn main() {
// ".into()); // ".into());
let mut lexer = lexer::Lexer::new(" let mut lexer = lexer::Lexer::new("
fn main() -> i32 { fn main() -> i32 {
if is_three(add_four(-1)) { let n = -1;
if is_three(add_four(n)) {
20 20
} else { } else {
30 30
} }
} }
fn add_four(n: i32) -> i32 { pub fn add_four(n: i32) -> i32 {
n + 4 n + 4
} }
fn is_three(n: i32) -> bool { fn is_three(n: i32) -> bool {
n == 3 n == 3
} }
fn add_four_float(n: f64) -> f64 {
n + 4.0
}
".into()); ".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![]; let mut tokens = vec![];
loop { loop {
@ -89,17 +100,7 @@ fn main() {
loop { loop {
match parser.next() { match parser.next() {
Ok(None) => break, Ok(None) => break,
Ok(Some(tree)) => { Ok(Some(tree)) => statements.push(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);
}
Err(error) => { Err(error) => {
eprintln!("error: {:?}", error); eprintln!("error: {:?}", error);
return; 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); panic!("{:?}", err);
} }
} }

View file

@ -47,7 +47,7 @@ impl Parser {
fn parse_statement(&mut self) -> Result<Option<Statement>, Error> { fn parse_statement(&mut self) -> Result<Option<Statement>, Error> {
let Some(tok) = self.peek_tok() else { let Some(tok) = self.peek_tok() else {
return Err(Error::SyntaxError(format!("unexpected eof"))); return Err(Error::syn("unexpected eof"));
}; };
let stmt = match tok { let stmt = match tok {
Token::Let => { Token::Let => {
@ -58,7 +58,14 @@ impl Parser {
self.eat(Token::Symbol(Symbol::Semicolon))?; self.eat(Token::Symbol(Symbol::Semicolon))?;
Statement::Let(name, expr) 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)?; self.eat(Token::Fn)?;
let name = self.parse_ident()?; let name = self.parse_ident()?;
self.eat(Token::OpenParan)?; self.eat(Token::OpenParan)?;
@ -86,7 +93,7 @@ impl Parser {
self.eat(Token::OpenBrace)?; self.eat(Token::OpenBrace)?;
let block = self.parse_block()?; let block = self.parse_block()?;
self.eat(Token::CloseBrace)?; 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)?; let expr = self.parse_expr(0)?;
@ -103,29 +110,29 @@ impl Parser {
fn parse_type(&mut self) -> Result<Type, Error> { fn parse_type(&mut self) -> Result<Type, Error> {
let Some(tok) = self.next_tok() else { 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() { Token::Ident(ident) => match ident.as_str() {
"i32" => Ok(Type::Integer), "i32" => Type::Integer,
"bool" => Ok(Type::Boolean), "f64" => Type::Float,
"bool" => Type::Boolean,
_ => return Err(Error::TypeError(format!("unknown type {ident}"))), _ => return Err(Error::TypeError(format!("unknown type {ident}"))),
}, },
_ => todo!(), _ => todo!(),
} };
Ok(ty)
} }
fn parse_ident(&mut self) -> Result<String, Error> { fn parse_ident(&mut self) -> Result<String, Error> {
match self.next_tok() { match self.next_tok() {
Some(Token::Ident(ident)) => Ok(ident.to_string()), Some(Token::Ident(ident)) => Ok(ident.to_string()),
Some(tk) => { Some(tk) => {
return Err(Error::SyntaxError(format!( Err(Error::SyntaxError(format!(
"expected identifier, got {tk:?}" "expected identifier, got {tk:?}"
))) )))
} }
None => { None => Err(Error::syn("expected identifier, got eof")),
return Err(Error::SyntaxError(format!("expected identifier, got eof")))
}
} }
} }
@ -142,7 +149,7 @@ impl Parser {
statements.push(stmt); statements.push(stmt);
match self.peek_tok() { match self.peek_tok() {
Some(Token::CloseBrace) => break, 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")), None => return Err(Error::syn("unexpected eof")),
}; };
} }
@ -153,7 +160,6 @@ impl Parser {
// Some(_) => return Err(Error::SyntaxError(format!("unexpected token {tok:?}"))), // Some(_) => return Err(Error::SyntaxError(format!("unexpected token {tok:?}"))),
// tok => 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)) Ok(Block(statements))
@ -172,7 +178,7 @@ impl Parser {
Token::Ident(ident) => { Token::Ident(ident) => {
let ident = ident.clone(); let ident = ident.clone();
if self.peek_tok().is_some_and(|t| *t == Token::OpenParan) { if self.peek_tok().is_some_and(|t| *t == Token::OpenParan) {
self.eat(Token::OpenParan); self.eat(Token::OpenParan)?;
let mut params = vec![]; let mut params = vec![];
while !self.peek_tok().is_some_and(|tok| tok == &Token::CloseParan) { while !self.peek_tok().is_some_and(|tok| tok == &Token::CloseParan) {
params.push(self.parse_expr(0)?); params.push(self.parse_expr(0)?);
@ -182,7 +188,7 @@ impl Parser {
break; break;
} }
} }
self.eat(Token::CloseParan); self.eat(Token::CloseParan)?;
Expr::Call(ident, params) Expr::Call(ident, params)
} else { } else {
Expr::Variable(ident) Expr::Variable(ident)
@ -190,6 +196,7 @@ impl Parser {
} }
Token::False => Expr::Literal(Literal::Boolean(false)), Token::False => Expr::Literal(Literal::Boolean(false)),
Token::True => Expr::Literal(Literal::Boolean(true)), 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::Char(ch) => Expr::Literal(Literal::Char(*ch)),
Token::If => { Token::If => {
let cond = self.parse_expr(0)?; let cond = self.parse_expr(0)?;
@ -222,7 +229,7 @@ impl Parser {
Expr::Match(Box::new(cond), map) Expr::Match(Box::new(cond), map)
} }
Token::Symbol(_) => { 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:?}"))); return Err(Error::SyntaxError(format!("unexpected token {tok:?}")));
}; };
let expr = self.parse_expr(1)?; let expr = self.parse_expr(1)?;

BIN
test.wasm Normal file

Binary file not shown.

View file

@ -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
)
)