function parameters

This commit is contained in:
tezlm 2023-10-02 00:48:28 -07:00
parent 7f49c2d7a3
commit bc02423fbd
Signed by: tezlm
GPG key ID: 649733FCD94AFBBA
5 changed files with 125 additions and 63 deletions

View file

@ -38,12 +38,20 @@ pub enum Statement {
Let(String, Expr), Let(String, Expr),
// Type(String, Type), // Type(String, Type),
Expr(Expr), Expr(Expr),
Func(String, Type, Block), Func(Func),
// Break, // Break,
// Continue, // Continue,
// Type, // Type,
} }
#[derive(Debug, Clone)]
pub struct Func {
pub name: String,
pub params: Vec<(String, Type)>,
pub ret: Type,
pub block: Block
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Block(pub Vec<Statement>); pub struct Block(pub Vec<Statement>);

View file

@ -5,9 +5,9 @@ optimizations
- write negative numberss directly instead of as positive + sign flip - write negative numberss directly instead of as positive + sign flip
*/ */
use crate::Error; use crate::data::{self, BinaryOp, Block, Expr, Func, Literal, Pattern, PrefixOp, Statement, Type};
use crate::data::{Expr, Literal, BinaryOp, PrefixOp, Statement, Pattern, Type, self, Block};
use crate::parser::Context; use crate::parser::Context;
use crate::Error;
pub struct Generator { pub struct Generator {
output: Box<dyn std::io::Write>, output: Box<dyn std::io::Write>,
@ -15,9 +15,7 @@ pub struct Generator {
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 { Generator { output }
output
}
} }
fn write_module(&mut self, stmts: &[Statement]) -> Result<(), Error> { fn write_module(&mut self, stmts: &[Statement]) -> Result<(), Error> {
@ -25,40 +23,54 @@ impl Generator {
write!(self.output, "(module"); write!(self.output, "(module");
for stmt in stmts { for stmt in stmts {
match stmt { match stmt {
Statement::Func(name, ret, block) => { Statement::Func(Func { name, ret, params, .. }) => {
ctx.locals.insert(name.clone(), ret.clone()); ctx.funcs.insert(name.clone(), (params.clone(), ret.clone()));
}, }
Statement::Let(..) | Statement::Expr(..) => return Err(Error::syn("incorrect top level statement")), Statement::Let(..) | Statement::Expr(..) => {
return Err(Error::syn("incorrect top level statement"))
}
}; };
} }
for stmt in stmts { for stmt in stmts {
match stmt { match stmt {
Statement::Func(name, ret, block) => self.write_func(&ctx, name, ret, block)?, Statement::Func(func) => self.write_func(&ctx, func)?,
Statement::Let(..) | Statement::Expr(..) => return Err(Error::syn("incorrect top level statement")), Statement::Let(..) | Statement::Expr(..) => {
return Err(Error::syn("incorrect top level statement"))
}
}; };
} }
write!(self.output, ")"); write!(self.output, ")");
Ok(()) Ok(())
} }
fn write_func(&mut self, ctx: &Context, name: &str, ret: &Type, block: &Block) -> Result<(), Error> { fn write_func(&mut self, ctx: &Context, func: &Func) -> Result<(), Error> {
write!(self.output, " (func ${name}"); write!(self.output, " (func ${}", func.name);
if name == "main" { if func.name == "main" {
write!(self.output, " (export \"_start\")"); write!(self.output, " (export \"_start\")");
} }
if !ret.is_empty() {
write!(self.output, " (result {})", ret.string());
}
write!(self.output, " (local $match i32)");
writeln!(self.output);
let expr = Expr::Block(block.clone()); let expr = Expr::Block(func.block.clone());
let mut ctx = ctx.clone(); let mut ctx = ctx.clone();
let mut exprs = Vec::new(); let mut exprs = Vec::new();
let inferred = block.infer(&ctx)?; for (name, ty) in &func.params {
if ret != &inferred { ctx.locals.insert(name.clone(), ty.clone());
return Err(Error::TypeError(format!("fn should return {ret:?}, but instead returns {inferred:?}"))); write!(self.output, " (param ${} {})", name, ty.string());
}
if !func.ret.is_empty() {
write!(self.output, " (result {})", func.ret.string());
}
writeln!(self.output);
writeln!(self.output, "(local $match i32)");
let inferred = func.block.infer(&ctx)?;
if func.ret != inferred {
return Err(Error::TypeError(format!(
"fn should return {:?}, but instead returns {inferred:?}",
func.ret
)));
} }
get_locals(&expr, &mut ctx, &mut exprs); get_locals(&expr, &mut ctx, &mut exprs);
@ -85,12 +97,6 @@ impl Generator {
} }
pub fn generate(statements: &[Statement]) -> Result<(), Error> { pub fn generate(statements: &[Statement]) -> Result<(), Error> {
let expr = match &statements[0] {
Statement::Expr(expr) => expr.clone(),
Statement::Func(name, ty, block) => data::Expr::Block(data::Block(vec![Statement::Func(name.clone(), ty.clone(), block.clone())])),
_ => todo!(),
};
let mut gen = Generator::new(Box::new(std::io::stdout())); let mut gen = Generator::new(Box::new(std::io::stdout()));
gen.write_module(statements) gen.write_module(statements)
} }
@ -102,7 +108,7 @@ fn gen_expr(expr: &Expr, ctx: &Context) {
Literal::Float(f) => println!("f64.const {f}"), Literal::Float(f) => println!("f64.const {f}"),
Literal::Boolean(b) => println!("i32.const {}", if *b { 1 } else { 0 }), Literal::Boolean(b) => println!("i32.const {}", if *b { 1 } else { 0 }),
_ => todo!(), _ => todo!(),
} },
Expr::Variable(name) => { Expr::Variable(name) => {
println!("local.get ${name}"); println!("local.get ${name}");
} }
@ -122,7 +128,7 @@ fn gen_expr(expr: &Expr, ctx: &Context) {
BinaryOp::Sub => println!("{ty}.sub"), BinaryOp::Sub => println!("{ty}.sub"),
BinaryOp::Div => println!("{ty}.div_u"), // do i _u or _s? BinaryOp::Div => println!("{ty}.div_u"), // do i _u or _s?
BinaryOp::Mod => println!("{ty}.rem_u"), BinaryOp::Mod => println!("{ty}.rem_u"),
BinaryOp::Eq => println!("{ty}.eq"), BinaryOp::Eq => println!("{ty}.eq"),
BinaryOp::Neq => println!("{ty}.neq"), BinaryOp::Neq => println!("{ty}.neq"),
BinaryOp::Less => println!("{ty}.lt_u"), BinaryOp::Less => println!("{ty}.lt_u"),
BinaryOp::Greater => println!("{ty}.gt_u"), BinaryOp::Greater => println!("{ty}.gt_u"),
@ -158,19 +164,16 @@ fn gen_expr(expr: &Expr, ctx: &Context) {
} }
// FIXME: awful code until i fix patching up parser and lexer // FIXME: awful code until i fix patching up parser and lexer
Expr::Match(cond, arms) => { Expr::Match(cond, arms) => {
println!(";; --- set match variable");
gen_expr(cond, ctx); gen_expr(cond, ctx);
println!("local.set $match"); println!("local.set $match");
println!(";; --- generate match");
for (idx, (pat, expr)) in arms.iter().enumerate() { for (idx, (pat, expr)) in arms.iter().enumerate() {
match pat { match pat {
Pattern::Literal(lit) => match lit { Pattern::Literal(lit) => match lit {
Literal::Integer(int) => println!("i32.const {}", int), Literal::Integer(int) => println!("i32.const {}", int),
Literal::Boolean(b) => println!("i32.const {}", if *b { 1 } else { 0 }), Literal::Boolean(b) => println!("i32.const {}", if *b { 1 } else { 0 }),
_ => todo!(), _ => todo!(),
} },
}; };
println!("local.get $match"); println!("local.get $match");
@ -186,7 +189,6 @@ fn gen_expr(expr: &Expr, ctx: &Context) {
} }
} }
println!("{}", ")".repeat(arms.len() * 2)); println!("{}", ")".repeat(arms.len() * 2));
println!(";; --- done");
} }
Expr::Block(b) => { Expr::Block(b) => {
for (i, stmt) in b.0.iter().enumerate() { for (i, stmt) in b.0.iter().enumerate() {
@ -197,11 +199,14 @@ fn gen_expr(expr: &Expr, ctx: &Context) {
println!("drop"); println!("drop");
} }
} }
_ => {}, _ => {}
} }
} }
} }
Expr::Call(func, _) => { Expr::Call(func, args) => {
for expr in args {
gen_expr(expr, ctx);
}
println!("call ${func}"); println!("call ${func}");
} }
}; };
@ -218,12 +223,12 @@ fn get_locals(expr: &Expr, ctx: &mut Context, exprs: &mut Vec<(String, Expr)>) {
exprs.push((name.clone(), expr.clone())); exprs.push((name.clone(), expr.clone()));
} }
Statement::Expr(expr) => get_locals(&expr, ctx, exprs), Statement::Expr(expr) => get_locals(&expr, ctx, exprs),
Statement::Func(name, ret, ..) => { Statement::Func(Func { name, ret, .. }) => {
ctx.locals.insert(name.clone(), todo!()); ctx.locals.insert(name.clone(), todo!());
}, }
} }
} }
}, }
Expr::Unary(_, expr) => get_locals(&expr, ctx, exprs), Expr::Unary(_, expr) => get_locals(&expr, ctx, exprs),
Expr::Binary(_, a, b) => { Expr::Binary(_, a, b) => {
get_locals(&a, ctx, exprs); get_locals(&a, ctx, exprs);

View file

@ -39,17 +39,26 @@ fn main() {
// } // }
// } // }
// ".into()); // ".into());
// let mut lexer = lexer::Lexer::new("
// fn main() -> i32 {
// two() * three()
// }
// fn two() -> i32 {
// (2 * 2) / 2
// }
// fn three() -> i32 {
// 10 * 6 / 20
// }
// ".into());
let mut lexer = lexer::Lexer::new(" let mut lexer = lexer::Lexer::new("
fn main() -> i32 { fn main() -> i32 {
two() * three() add_three(30)
} }
fn two() -> i32 { fn add_three(n: i32) -> i32 {
(2 * 2) / 2 n + 3
}
fn three() -> i32 {
10 * 6 / 20
} }
".into()); ".into());

View file

@ -1,6 +1,6 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::data::{BinaryOp, Block, Expr, Literal, Pattern, PrefixOp, Statement, Type}; use crate::data::{BinaryOp, Block, Expr, Literal, Pattern, PrefixOp, Statement, Type, Func};
use crate::lexer::{Token, Symbol}; use crate::lexer::{Token, Symbol};
use crate::Error; use crate::Error;
@ -12,6 +12,7 @@ pub struct Parser {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Context { pub struct Context {
pub locals: HashMap<String, Type>, pub locals: HashMap<String, Type>,
pub funcs: HashMap<String, (Vec<(String, Type)>, Type)>,
} }
impl Parser { impl Parser {
@ -60,7 +61,20 @@ impl Parser {
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)?;
// TODO: parameters let mut params = vec![];
while !self.peek_tok().is_some_and(|tok| tok == &Token::CloseParan) {
let name = self.parse_ident()?;
self.eat(Token::Symbol(Symbol::Colon))?;
let ty = self.parse_type()?;
params.push((name, ty));
if self.peek_tok() == Some(&Token::Symbol(Symbol::Comma)) {
self.eat(Token::Symbol(Symbol::Comma))?;
} else {
break;
}
}
self.eat(Token::CloseParan)?; self.eat(Token::CloseParan)?;
let ret = if self.peek_tok().is_some_and(|tok| tok == &Token::Symbol(Symbol::ThinArrow)) { let ret = if self.peek_tok().is_some_and(|tok| tok == &Token::Symbol(Symbol::ThinArrow)) {
self.eat(Token::Symbol(Symbol::ThinArrow))?; self.eat(Token::Symbol(Symbol::ThinArrow))?;
@ -71,7 +85,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(name, ret, block) Statement::Func(Func { name, params, ret, block })
} }
_ => Statement::Expr(self.parse_expr(0)?), _ => Statement::Expr(self.parse_expr(0)?),
}; };
@ -138,9 +152,17 @@ impl Parser {
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);
// TODO: function parameters let mut params = vec![];
while !self.peek_tok().is_some_and(|tok| tok == &Token::CloseParan) {
params.push(self.parse_expr(0)?);
if self.peek_tok() == Some(&Token::Symbol(Symbol::Comma)) {
self.eat(Token::Symbol(Symbol::Comma))?;
} else {
break;
}
}
self.eat(Token::CloseParan); self.eat(Token::CloseParan);
Expr::Call(ident, vec![]) Expr::Call(ident, params)
} else { } else {
Expr::Variable(ident) Expr::Variable(ident)
} }

View file

@ -44,8 +44,25 @@ impl Expr {
Ok(match_ty) Ok(match_ty)
} }
Self::Block(block) => block.infer(ctx), Self::Block(block) => block.infer(ctx),
Self::Call(name, ..) => match ctx.locals.get(name) { Self::Call(name, args) => match ctx.funcs.get(name) {
Some(ty) => Ok(ty.clone()), Some((params, ret)) => {
if args.len() < params.len() {
return Err(Error::ty("missing parameters"));
}
if args.len() > params.len() {
return Err(Error::ty("too many parameters"));
}
for (arg, (name, ty)) in args.iter().zip(params) {
let got = arg.infer(ctx)?;
if got != *ty {
return Err(Error::TypeError(format!("wrong type for {name}: it should've been {ty:?} but you put a {got:?}", )));
}
}
Ok(ret.clone())
}
None => Err(Error::ReferenceError(format!( None => Err(Error::ReferenceError(format!(
"cannot find variable {name}" "cannot find variable {name}"
))), ))),
@ -149,6 +166,7 @@ impl Context {
pub fn new() -> Context { pub fn new() -> Context {
Context { Context {
locals: HashMap::new(), locals: HashMap::new(),
funcs: HashMap::new(),
} }
} }
} }