changes
This commit is contained in:
parent
06b97d3132
commit
0d81019cb1
9 changed files with 399 additions and 210 deletions
2
blank.html
Normal file
2
blank.html
Normal file
|
@ -0,0 +1,2 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- blank html file + miniserve because of cors -->
|
12
src/data.rs
12
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!(),
|
||||
|
|
|
@ -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<std::io::Error> for Error {
|
||||
fn from(value: std::io::Error) -> Self {
|
||||
Error::IoError(value)
|
||||
}
|
||||
}
|
||||
|
|
312
src/generator.rs
312
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<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 {
|
||||
pub fn new(output: Box<dyn std::io::Write>) -> 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(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate(statements: &[Statement]) -> Result<(), Error> {
|
||||
let mut gen = Generator::new(Box::new(std::io::stdout()));
|
||||
gen.write_module(statements)
|
||||
}
|
||||
|
||||
fn gen_expr(expr: &Expr, ctx: &Context) {
|
||||
fn gen_expr(&mut self, expr: &Expr, parent_gctx: &GenContext, ctx: &Context) -> Result<(), Error> {
|
||||
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 }),
|
||||
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) => {
|
||||
println!("local.get ${name}");
|
||||
writeln!(self.output, "local.get ${name}")?;
|
||||
}
|
||||
Expr::Binary(op, a, b) => {
|
||||
gen_expr(&a, ctx);
|
||||
gen_expr(&b, ctx);
|
||||
self.gen_expr(a, parent_gctx, ctx)?;
|
||||
self.gen_expr(b, parent_gctx, ctx)?;
|
||||
|
||||
let ty = match expr.infer(&ctx).unwrap() {
|
||||
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"),
|
||||
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) => println!("i32.const {}", -int),
|
||||
Literal::Float(f) => println!("f64.const {}", -f),
|
||||
_ => unreachable!(),
|
||||
Literal::Integer(int) => writeln!(self.output, "i32.const {}", -int)?,
|
||||
Literal::Float(f) => writeln!(self.output, "f64.const {}", -f)?,
|
||||
_ => todo!(),
|
||||
}
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
gen_expr(e, ctx);
|
||||
self.gen_expr(e, parent_gctx, ctx)?;
|
||||
match op {
|
||||
PrefixOp::Minus => {
|
||||
// this is so inefficent, but i don't care
|
||||
println!("i32.const -1");
|
||||
println!("i32.mul");
|
||||
writeln!(self.output, "i32.const -1")?;
|
||||
writeln!(self.output, "i32.mul")?;
|
||||
}
|
||||
PrefixOp::LogicNot => {
|
||||
println!("i32.eqz");
|
||||
writeln!(self.output, "i32.eqz")?;
|
||||
}
|
||||
PrefixOp::BitNot => {
|
||||
// TODO: do i flip the sign bit?
|
||||
println!("i32.const {}", i32::MAX);
|
||||
println!("i32.xor");
|
||||
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) => {
|
||||
gen_expr(cond, ctx);
|
||||
println!("local.set $match");
|
||||
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) => println!("i32.const {}", int),
|
||||
Literal::Boolean(b) => println!("i32.const {}", if *b { 1 } else { 0 }),
|
||||
Literal::Integer(int) => writeln!(self.output, "i32.const {}", int)?,
|
||||
Literal::Boolean(b) => writeln!(self.output, "i32.const {}", if *b { 1 } else { 0 })?,
|
||||
_ => todo!(),
|
||||
},
|
||||
};
|
||||
|
||||
println!("local.get $match");
|
||||
println!("i32.eq");
|
||||
println!("(if (result i32) (then");
|
||||
gen_expr(expr, ctx);
|
||||
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
|
||||
println!(") (else unreachable");
|
||||
writeln!(self.output, ") (else unreachable")?;
|
||||
} else {
|
||||
println!(") (else");
|
||||
writeln!(self.output, ") (else")?;
|
||||
}
|
||||
}
|
||||
println!("{}", ")".repeat(arms.len() * 2));
|
||||
writeln!(self.output, "{}", ")".repeat(arms.len() * 2))?;
|
||||
}
|
||||
Expr::Block(b) => {
|
||||
for (i, stmt) in b.0.iter().enumerate() {
|
||||
let mut ctx = ctx.clone();
|
||||
for (_i, stmt) in b.0.iter().enumerate() {
|
||||
match stmt {
|
||||
Statement::TailExpr(expr) => {
|
||||
gen_expr(expr, &ctx);
|
||||
self.gen_expr(expr, parent_gctx, &ctx)?;
|
||||
}
|
||||
Statement::Expr(expr) => {
|
||||
gen_expr(expr, &ctx);
|
||||
println!("drop");
|
||||
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 {
|
||||
gen_expr(expr, ctx);
|
||||
self.gen_expr(expr, parent_gctx, ctx)?;
|
||||
}
|
||||
println!("call ${func}");
|
||||
writeln!(self.output, "call ${func}")?;
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: block scoped variables
|
||||
fn get_locals(expr: &Expr, ctx: &mut Context, exprs: &mut Vec<(String, Expr)>) {
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct GenContext {
|
||||
exprs: Vec<(String, Expr)>,
|
||||
strings: Vec<(String, usize)>,
|
||||
}
|
||||
|
||||
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(..) => (),
|
||||
// }
|
||||
// }
|
||||
|
|
|
@ -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<Token, Error> {
|
||||
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 {
|
||||
|
|
61
src/main.rs
61
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ impl Parser {
|
|||
|
||||
fn parse_statement(&mut self) -> Result<Option<Statement>, 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<Type, Error> {
|
||||
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<String, Error> {
|
||||
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)?;
|
||||
|
|
BIN
test.wasm
Normal file
BIN
test.wasm
Normal file
Binary file not shown.
44
test.wat
44
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
|
||||
)
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue