diff --git a/fine/src/compiler.rs b/fine/src/compiler.rs index e4033581..92eb81ef 100644 --- a/fine/src/compiler.rs +++ b/fine/src/compiler.rs @@ -2,35 +2,10 @@ use std::collections::HashMap; use crate::{ parser::{SyntaxTree, Tree, TreeKind, TreeRef}, - semantics::{Location, Semantics, Type}, + semantics::{Declaration, Location, Semantics, Type}, tokens::TokenKind, }; -macro_rules! compiler_assert_eq { - ($compiler:expr, $tr:expr, $ll:expr, $rr:expr, $($t:tt)*) => {{ - let left = &$ll; - let right = &$rr; - if left != right { - let semantics = $compiler.semantics; - semantics.dump_compiler_state(Some($tr)); - - let message = format!($($t)*); - assert_eq!(left, right, "{}", message); - } - }}; -} - -macro_rules! compiler_assert { - ($compiler:expr, $tr:expr, $($t:tt)*) => {{ - if !($($t)*) { - let semantics = $compiler.semantics; - semantics.dump_compiler_state(Some($tr)); - - assert!($($t)*); - } - }}; -} - // TODO: If I were cool this would by actual bytecode. // But I'm not cool. #[derive(Debug)] @@ -56,6 +31,10 @@ pub enum Instruction { PushTrue, StoreLocal(usize), StoreModule(usize), + LoadFunction(usize), + LoadExtern(usize), + LoadExternFunction(usize), // NOTE: FUNKY, might want to indirect this index. + Call(usize), } pub enum Export { @@ -130,7 +109,9 @@ struct Compiler<'a> { semantics: &'a Semantics<'a>, syntax: &'a SyntaxTree<'a>, - function_bindings: HashMap, + // TODO: generic functions will actually be keyed by treeref and concrete + // types + function_bindings: HashMap, module: Module, function: Function, } @@ -158,6 +139,62 @@ impl<'a> Compiler<'a> { } } +macro_rules! compiler_assert_eq { + ($compiler:expr, $tr:expr, $ll:expr, $rr:expr $(,)?) => {{ + let left = &$ll; + let right = &$rr; + if left != right { + let semantics = $compiler.semantics; + semantics.dump_compiler_state(Some($tr)); + + assert_eq!(left, right); + } + }}; + + ($compiler:expr, $tr:expr, $ll:expr, $rr:expr, $($t:tt)+) => {{ + let left = &$ll; + let right = &$rr; + if left != right { + let semantics = $compiler.semantics; + semantics.dump_compiler_state(Some($tr)); + + assert_eq!(left, right, $($t)*); + } + }}; +} + +macro_rules! compiler_assert { + ($compiler:expr, $tr:expr, $cond:expr $(,)?) => {{ + if !$cond { + let semantics = $compiler.semantics; + semantics.dump_compiler_state(Some($tr)); + + assert!($cond); + } + }}; + + ($compiler:expr, $tr:expr, $cond:expr, $($arg:tt)+) => {{ + if !$cond { + let semantics = $compiler.semantics; + semantics.dump_compiler_state(Some($tr)); + + assert!($cond, $($arg)*); + } + }}; +} + +macro_rules! ice { + ($compiler: expr, $tr:expr, $($t:tt)+) => {{ + let semantics = $compiler.semantics; + semantics.dump_compiler_state(Some($tr)); + panic!($($t)*) + }} +} + +// macro_rules! ice { +// ($compiler:expr, $tr:expr, $($t:tt)*) => {{}}; +// } + pub fn compile(semantics: &Semantics) -> Module { let mut compiler = Compiler { semantics, @@ -198,15 +235,14 @@ fn compile_expression(c: &mut Compiler, t: TreeRef) { TreeKind::Error => None, TreeKind::LiteralExpression => compile_literal(c, t, tree), TreeKind::GroupingExpression => compile_grouping(c, tree), - TreeKind::UnaryExpression => compile_unary_operator(c, tree), + TreeKind::UnaryExpression => compile_unary_operator(c, t, tree), TreeKind::ConditionalExpression => compile_condition_expression(c, tree), - TreeKind::BinaryExpression => compile_binary_expression(c, tree), + TreeKind::BinaryExpression => compile_binary_expression(c, t, tree), TreeKind::Identifier => compile_identifier_expression(c, t, tree), - TreeKind::CallExpression => todo!(), + TreeKind::CallExpression => compile_call_expression(c, tree), TreeKind::Block => compile_block_expression(c, tree), - _ => c - .semantics - .internal_compiler_error(Some(t), "tree is not an expression, cannot compile"), + TreeKind::Argument => compile_argument(c, tree), + _ => ice!(c, t, "{tree:?} is not an expression, cannot compile"), }; if matches!(cr, None) { c.push(Instruction::Panic); @@ -246,7 +282,7 @@ fn compile_literal(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { c.push(Instruction::PushString(index)) } Type::Error => c.push(Instruction::Panic), - _ => panic!("unsupported literal type: {t:?}"), + _ => ice!(c, t, "unsupported literal type: {t:?}"), }; OK } @@ -256,10 +292,10 @@ fn compile_grouping(c: &mut Compiler, t: &Tree) -> CR { OK } -fn compile_unary_operator(c: &mut Compiler, t: &Tree) -> CR { - compile_expression(c, t.nth_tree(1)?); +fn compile_unary_operator(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { + compile_expression(c, tr.nth_tree(1)?); - let tok = t.nth_token(0)?; + let tok = tr.nth_token(0)?; match tok.kind { TokenKind::Minus => { c.push(Instruction::PushFloat(-1.0)); @@ -268,7 +304,7 @@ fn compile_unary_operator(c: &mut Compiler, t: &Tree) -> CR { TokenKind::Bang => { c.push(Instruction::BoolNot); } - _ => panic!("unsupported unary operator"), + _ => ice!(c, t, "unsupported unary operator"), } OK } @@ -294,23 +330,23 @@ fn compile_condition_expression(c: &mut Compiler, t: &Tree) -> CR { OK } -fn compile_binary_expression(c: &mut Compiler, t: &Tree) -> CR { - compile_expression(c, t.nth_tree(0)?); - match t.nth_token(1)?.kind { +fn compile_binary_expression(c: &mut Compiler, t: TreeRef, tr: &Tree) -> CR { + compile_expression(c, tr.nth_tree(0)?); + match tr.nth_token(1)?.kind { TokenKind::Plus => { - compile_expression(c, t.nth_tree(2)?); + compile_expression(c, tr.nth_tree(2)?); c.push(Instruction::FloatAdd); } TokenKind::Minus => { - compile_expression(c, t.nth_tree(2)?); + compile_expression(c, tr.nth_tree(2)?); c.push(Instruction::FloatSubtract); } TokenKind::Star => { - compile_expression(c, t.nth_tree(2)?); + compile_expression(c, tr.nth_tree(2)?); c.push(Instruction::FloatMultiply); } TokenKind::Slash => { - compile_expression(c, t.nth_tree(2)?); + compile_expression(c, tr.nth_tree(2)?); c.push(Instruction::FloatDivide); } TokenKind::And => { @@ -321,7 +357,7 @@ fn compile_binary_expression(c: &mut Compiler, t: &Tree) -> CR { c.patch(jump_false_index, |i| Instruction::JumpFalse(i)); - compile_expression(c, t.nth_tree(2)?); + compile_expression(c, tr.nth_tree(2)?); c.patch(jump_end_index, |i| Instruction::Jump(i)); } @@ -333,11 +369,11 @@ fn compile_binary_expression(c: &mut Compiler, t: &Tree) -> CR { c.patch(jump_true_index, |i| Instruction::JumpTrue(i)); - compile_expression(c, t.nth_tree(2)?); + compile_expression(c, tr.nth_tree(2)?); c.patch(jump_end_index, |i| Instruction::Jump(i)); } - _ => panic!("Unsupported binary expression"), + _ => ice!(c, t, "Unsupported binary expression"), } OK } @@ -347,28 +383,69 @@ fn compile_identifier_expression(c: &mut Compiler, t: TreeRef, tree: &Tree) -> O let environment = c.semantics.environment_of(t); let declaration = environment.bind(ident)?; - let instruction = match declaration.location { - Location::Local => { - if declaration.index >= c.function.locals { - c.function.locals = declaration.index + 1; + // TODO: Load function declaration. :P + let instruction = match declaration { + Declaration::Variable { + location, index, .. + } => { + let index = *index; + match location { + Location::Local => { + if index >= c.function.locals { + c.function.locals = index + 1; + } + Instruction::LoadLocal(index) + } + Location::Argument => { + compiler_assert!(c, t, index < c.function.args); + Instruction::LoadArgument(index) + } + Location::Module => { + compiler_assert!(c, t, index < c.module.globals); + Instruction::LoadModule(index) + } } - Instruction::LoadLocal(declaration.index) } - Location::Argument => { - compiler_assert!(c, t, declaration.index < c.function.args); - Instruction::LoadArgument(declaration.index) - } - Location::Module => { - compiler_assert!(c, t, declaration.index < c.module.globals); - Instruction::LoadModule(declaration.index) + Declaration::Function { declaration, .. } => { + let index = match c.function_bindings.get(declaration) { + Some(index) => *index, + None => { + let tree = &c.syntax[*declaration]; + compiler_assert_eq!(c, t, tree.kind, TreeKind::FunctionDecl); + compile_function_declaration(c, t, tree, false)?; + *c.function_bindings + .get(declaration) + .expect("did not compile the function!") + } + }; + Instruction::LoadFunction(index) } + Declaration::ExternFunction { id, .. } => Instruction::LoadExternFunction(id.id()), }; + c.push(instruction); OK } -fn compile_block_expression(c: &mut Compiler, tree: &Tree) -> Option<()> { +fn compile_call_expression(c: &mut Compiler, tree: &Tree) -> CR { + let arg_list = tree.child_tree_of_kind(c.syntax, TreeKind::ArgumentList)?; + let mut args: Vec<_> = arg_list.child_trees().collect(); + let arg_count = args.len(); + + args.reverse(); + for arg in args { + compile_expression(c, arg); + } + + let func = tree.nth_tree(0)?; + compile_expression(c, func); + + c.push(Instruction::Call(arg_count)); + OK +} + +fn compile_block_expression(c: &mut Compiler, tree: &Tree) -> CR { let last_is_brace = tree.nth_token(tree.children.len() - 1).is_some(); let last_index = tree.children.len() - if last_is_brace { 2 } else { 1 }; @@ -379,14 +456,19 @@ fn compile_block_expression(c: &mut Compiler, tree: &Tree) -> Option<()> { OK } +fn compile_argument(c: &mut Compiler, tree: &Tree) -> CR { + compile_expression(c, tree.nth_tree(0)?); + OK +} + fn compile_statement(c: &mut Compiler, t: TreeRef, gen_value: bool) { let tree = &c.semantics.tree()[t]; let cr = match tree.kind { - TreeKind::FunctionDecl => compile_function_declaration(c, tree, gen_value), + TreeKind::FunctionDecl => compile_function_declaration(c, t, tree, gen_value), TreeKind::LetStatement => compile_let_statement(c, t, tree, gen_value), TreeKind::ExpressionStatement => compile_expression_statement(c, tree, gen_value), TreeKind::IfStatement => compile_if_statement(c, tree, gen_value), - _ => panic!("unsupported tree kind {:?}", tree.kind), + _ => ice!(c, t, "unsupported tree kind {:?}", tree.kind), }; if matches!(cr, None) { c.push(Instruction::Panic); @@ -424,20 +506,28 @@ fn compile_let_statement(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: b let environment = c.semantics.environment_of(t); let declaration = environment.bind(tree.nth_token(1)?)?; - let instruction = match declaration.location { + let Declaration::Variable { + location, index, .. + } = declaration + else { + ice!(c, t, "let cannot make a non-variable declaration") + }; + + let index = *index; + let instruction = match location { Location::Local => { - if declaration.index >= c.function.locals { - c.function.locals = declaration.index + 1; + if index >= c.function.locals { + c.function.locals = index + 1; } - Instruction::StoreLocal(declaration.index) + Instruction::StoreLocal(index) } Location::Module => { - if declaration.index >= c.module.globals { - c.module.globals = declaration.index + 1; + if index >= c.module.globals { + c.module.globals = index + 1; } - Instruction::StoreModule(declaration.index) + Instruction::StoreModule(index) } - _ => panic!("unsuitable location for let declaration"), + _ => ice!(c, t, "unsuitable location for let declaration"), }; c.push(instruction); if gen_value { @@ -447,30 +537,26 @@ fn compile_let_statement(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: b OK } -fn compile_function_declaration(c: &mut Compiler, tree: &Tree, gen_value: bool) -> CR { - let name = tree.nth_token(1)?; - let block = if tree - .nth_token(3) - .is_some_and(|t| t.kind == TokenKind::Arrow) - { - tree.nth_tree(4)? - } else { - tree.nth_tree(3)? - }; +fn compile_function_declaration(c: &mut Compiler, t: TreeRef, tree: &Tree, gen_value: bool) -> CR { + // Only compile a given function once. + // TODO: This should actually be compiled on access! How is this going to work?? + if !c.function_bindings.contains_key(&t) { + let name = tree.nth_token(1)?; - let arg_list = tree.nth_tree(2)?; - let arg_count = c.syntax[arg_list].children.len() - 2; + let block = tree.child_of_kind(c.syntax, TreeKind::Block)?; + let param_list = tree.child_tree_of_kind(c.syntax, TreeKind::ParamList)?; + let param_count = param_list.children.len() - 2; - let mut prev = Function::new(name.as_str(), arg_count); - std::mem::swap(&mut c.function, &mut prev); + let mut prev = Function::new(name.as_str(), param_count); + std::mem::swap(&mut c.function, &mut prev); - c.function_bindings - .insert(c.function.name.clone(), c.module.functions.len()); + c.function_bindings.insert(t, c.module.functions.len()); - compile_expression(c, block); + compile_expression(c, block); - std::mem::swap(&mut c.function, &mut prev); - c.module.functions.push(prev); + std::mem::swap(&mut c.function, &mut prev); + c.module.functions.push(prev); + } if gen_value { c.push(Instruction::PushNothing); diff --git a/fine/src/parser.rs b/fine/src/parser.rs index 13cdd620..8bc8c0ce 100644 --- a/fine/src/parser.rs +++ b/fine/src/parser.rs @@ -138,6 +138,7 @@ pub enum TreeKind { BinaryExpression, IfStatement, Identifier, + ReturnType, } pub struct Tree<'a> { @@ -169,6 +170,31 @@ impl<'a> Tree<'a> { .flatten() } + pub fn child_trees<'b>(&'b self) -> impl Iterator + 'b { + self.children.iter().filter_map(|c| match c { + Child::Tree(t) => Some(*t), + _ => None, + }) + } + + pub fn child_of_kind(&self, s: &SyntaxTree, kind: TreeKind) -> Option { + self.children + .iter() + .filter_map(|c| match c { + Child::Tree(t) => Some(*t), + _ => None, + }) + .find(|c| s[*c].kind == kind) + } + + pub fn child_tree_of_kind<'b>( + &'b self, + s: &'b SyntaxTree<'a>, + kind: TreeKind, + ) -> Option<&'b Tree<'a>> { + self.child_of_kind(s, kind).map(|t| &s[t]) + } + pub fn dump(&self, tree: &SyntaxTree<'a>, with_positions: bool, output: &mut String) { let _ = write!(output, "{:?}", self.kind); if with_positions { @@ -481,8 +507,8 @@ fn function(p: &mut CParser) { if p.at(TokenKind::LeftParen) { param_list(p); } - if p.eat(TokenKind::Arrow) { - type_expr(p); + if p.at(TokenKind::Arrow) { + return_type(p); } if p.at(TokenKind::LeftBrace) { block(p); @@ -525,6 +551,19 @@ fn parameter(p: &mut CParser) { p.end(m, TreeKind::Parameter); } +fn return_type(p: &mut CParser) { + assert!(p.at(TokenKind::Arrow)); + let m = p.start(); + + p.expect( + TokenKind::Arrow, + "function return type starts with an arrow", + ); + type_expr(p); + + p.end(m, TreeKind::ReturnType); +} + fn type_expr(p: &mut CParser) { let m = p.start(); // TODO: Other kinds of type expressions probably! diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index d9ef011b..3de5c052 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -57,7 +57,7 @@ impl fmt::Display for Error { } } -#[derive(Copy, Clone)] +#[derive(Clone)] pub enum Type { // Signals a type error. If you receive this then you know that an error // has already been reported; if you produce this be sure to also note @@ -69,12 +69,17 @@ pub enum Type { // everything's fine. Unreachable, + // This is until generics are working + MagicPrintGarbage, + Nothing, // TODO: Numeric literals should be implicitly convertable, unlike other // types. Maybe just "numeric literal" type? F64, String, Bool, + + Function(Vec>, Box), } impl Type { @@ -119,50 +124,98 @@ impl fmt::Display for Type { F64 => write!(f, "f64"), String => write!(f, "string"), Bool => write!(f, "bool"), + MagicPrintGarbage => write!(f, "MagicPrintGarbage"), + Function(args, ret) => { + write!(f, "(")?; + let mut first = true; + for arg in args.iter() { + if !first { + write!(f, ", ")?; + } + write!(f, "{arg}")?; + first = false; + } + write!(f, ") -> {ret}") + } } } } -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub enum Location { Argument, Local, Module, } -pub struct Declaration { - pub declaration_type: Type, - pub location: Location, - pub index: usize, +// TODO: Is `usize` what we want? Do we want e.g. dyn trait for invoke? +#[derive(Clone, Debug)] +pub struct ExternalFunctionId(usize); + +impl ExternalFunctionId { + pub fn id(&self) -> usize { + self.0 + } +} + +pub enum Declaration { + Variable { + declaration_type: Type, + location: Location, + index: usize, + }, + Function { + declaration_type: Type, + declaration: TreeRef, //? + }, + ExternFunction { + declaration_type: Type, + id: ExternalFunctionId, + }, } pub struct Environment { pub parent: Option, pub location: Location, - pub base_index: usize, + pub next_index: usize, pub declarations: HashMap, Declaration>, } impl Environment { - pub fn new(parent: Option, location: Location, base_index: usize) -> Self { + pub fn new(parent: Option, location: Location) -> Self { + let parent_location = parent + .as_ref() + .map(|p| p.location) + .unwrap_or(Location::Module); + let base = parent.as_ref().map(|p| p.next_index).unwrap_or(0); + let next_index = match (parent_location, location) { + (_, Location::Argument) => 0, + + (Location::Local, Location::Local) => base, + (_, Location::Local) => 0, + + (Location::Module, Location::Module) => base, + (_, Location::Module) => panic!("What?"), + }; + Environment { parent, location, - base_index, + next_index, declarations: HashMap::new(), } } pub fn insert(&mut self, token: &Token, t: Type) { - let index = self.base_index + self.declarations.len(); self.declarations.insert( token.as_str().into(), - Declaration { + Declaration::Variable { declaration_type: t, location: self.location, - index, + index: self.next_index, }, ); + self.next_index += 1; } pub fn bind(&self, token: &Token) -> Option<&Declaration> { @@ -237,6 +290,24 @@ fn set_logical_parents( } } } + TreeKind::FunctionDecl => { + // In a function declaration, the logical parent of the body is + // the parameter list. + let param_list = tree.child_of_kind(syntax_tree, TreeKind::ParamList); + let body = tree.child_of_kind(syntax_tree, TreeKind::Block); + for child in &tree.children { + match child { + Child::Token(_) => (), + Child::Tree(ct) => { + if Some(*ct) == body { + set_logical_parents(parents, syntax_tree, *ct, param_list); + } else { + set_logical_parents(parents, syntax_tree, *ct, Some(t)); + } + } + } + } + } _ => { // By default, the parent for each child is current tree. for child in &tree.children { @@ -270,7 +341,7 @@ pub struct Semantics<'a> { errors: RefCell>, types: RefCell>>, environments: RefCell>>, - empty_environment: EnvironmentRef, + root_environment: EnvironmentRef, } impl<'a> Semantics<'a> { @@ -280,6 +351,15 @@ impl<'a> Semantics<'a> { set_logical_parents(&mut logical_parents, tree, root, None); } + let mut root_environment = Environment::new(None, Location::Module); + root_environment.declarations.insert( + "print".into(), + Declaration::ExternFunction { + declaration_type: Type::MagicPrintGarbage, + id: ExternalFunctionId(0), + }, + ); + let mut semantics = Semantics { syntax_tree: tree, lines, @@ -287,7 +367,7 @@ impl<'a> Semantics<'a> { errors: RefCell::new(vec![]), types: RefCell::new(vec![Incremental::None; tree.len()]), environments: RefCell::new(vec![Incremental::None; tree.len()]), - empty_environment: EnvironmentRef::new(Environment::new(None, Location::Module, 0)), + root_environment: EnvironmentRef::new(root_environment), }; // NOTE: We ensure all the known errors are reported before we move @@ -379,7 +459,7 @@ impl<'a> Semantics<'a> { Incremental::Complete(e) => return e.clone(), Incremental::InProgress => { // NOTE: Set the state so the ICE doesn't loop on itself. - *state = Incremental::Complete(self.empty_environment.clone()); + *state = Incremental::Complete(self.root_environment.clone()); drop(borrow); //eprintln!("environment_of circular => {t:?}"); @@ -394,12 +474,14 @@ impl<'a> Semantics<'a> { let parent = match self.logical_parents[t.index()] { Some(t) => self.environment_of(t), - None => self.empty_environment.clone(), + None => self.root_environment.clone(), }; let result = match tree.kind { TreeKind::LetStatement => self.environment_of_let(parent, tree), - TreeKind::FunctionDecl => self.environment_of_func(parent, tree), + TreeKind::ParamList => self.environment_of_paramlist(parent, tree), + TreeKind::File => self.environment_of_file(parent, tree), + TreeKind::Block => self.environment_of_block(parent, tree), // TODO: Blocks should introduce a local environment if required. // Test with a type error in a block statement and a @@ -426,6 +508,62 @@ impl<'a> Semantics<'a> { result } + fn environment_of_block(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef { + let mut environment = Environment::new(Some(parent), Location::Local); + for child in tree.children.iter() { + match child { + Child::Tree(t) => { + let ct = &self.syntax_tree[*t]; + if ct.kind == TreeKind::FunctionDecl { + // TODO: Should I have accessors for function decls? + let Some(name) = ct.nth_token(1) else { + continue; + }; + + environment.declarations.insert( + name.as_str().into(), + Declaration::Function { + declaration_type: self.type_of(*t), + declaration: *t, + }, + ); + } + } + _ => {} + } + } + + EnvironmentRef::new(environment) + } + + fn environment_of_file(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef { + let mut environment = Environment::new(Some(parent), Location::Module); + for child in tree.children.iter() { + match child { + Child::Tree(t) => { + let ct = &self.syntax_tree[*t]; + if ct.kind == TreeKind::FunctionDecl { + // TODO: Should I have accessors for function decls? + let Some(name) = ct.nth_token(1) else { + continue; + }; + + environment.declarations.insert( + name.as_str().into(), + Declaration::Function { + declaration_type: self.type_of(*t), + declaration: *t, + }, + ); + } + } + _ => {} + } + } + + EnvironmentRef::new(environment) + } + fn environment_of_let(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef { let Some(name) = tree.nth_token(1) else { return parent; // Error is already reported? @@ -440,35 +578,23 @@ impl<'a> Semantics<'a> { None => Type::Error, }; - let (location, base_index) = match parent.location { - Location::Local => ( - Location::Local, - parent.base_index + parent.declarations.len(), - ), - Location::Module => ( - Location::Module, - parent.base_index + parent.declarations.len(), - ), - Location::Argument => (Location::Local, 0), + let location = match parent.location { + Location::Local => Location::Local, + Location::Module => Location::Module, + Location::Argument => Location::Local, }; - let mut environment = Environment::new(Some(parent), location, base_index); + let mut environment = Environment::new(Some(parent), location); environment.insert(name, declaration_type); EnvironmentRef::new(environment) } - fn environment_of_func(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef { - let Some(param_list) = tree.nth_tree(2) else { - return parent; // SE - }; - let param_list = &self.syntax_tree[param_list]; - if param_list.kind != TreeKind::ParamList { - return parent; // SE - } + fn environment_of_paramlist(&self, parent: EnvironmentRef, tree: &Tree) -> EnvironmentRef { + assert!(tree.kind == TreeKind::ParamList); - let mut environment = Environment::new(Some(parent), Location::Argument, 0); - for child in param_list.children.iter() { + let mut environment = Environment::new(Some(parent), Location::Argument); + for child in tree.children.iter() { let Child::Tree(ct) = child else { continue; }; @@ -529,12 +655,18 @@ impl<'a> Semantics<'a> { TreeKind::ExpressionStatement => self.type_of_expression_statement(tree), TreeKind::Identifier => self.type_of_identifier(t, tree), + TreeKind::FunctionDecl => self.type_of_function_decl(tree), + _ => self.internal_compiler_error(Some(t), "asking for a nonsense type"), }; // NOTE: These return `None` if they encounter some problem. let result = result.unwrap_or(Type::Error); + if result.is_error() { + eprintln!("OH NO AN ERROR AT {}: {:?}", t.index(), tree); + } + self.types.borrow_mut()[t.index()] = Incremental::Complete(result.clone()); result } @@ -635,7 +767,10 @@ impl<'a> Semantics<'a> { "string" => Some(Type::String), "bool" => Some(Type::Bool), "()" => Some(Type::Nothing), - _ => Some(Type::Error), + _ => { + self.report_error_tree(tree, "Unrecognized type"); + Some(Type::Error) + } } } @@ -748,12 +883,64 @@ impl<'a> Semantics<'a> { fn type_of_call(&self, tree: &Tree) -> Option { assert_eq!(tree.kind, TreeKind::CallExpression); - Some(Type::Error) + + let f_ref = tree.nth_tree(0)?; + let f = self.type_of(f_ref); + + let arg_list = &self.syntax_tree[tree.nth_tree(1)?]; + let arg_types: Vec<_> = arg_list + .children + .iter() + .filter_map(|c| match c { + Child::Tree(t) => Some((*t, self.type_of(*t))), + _ => None, + }) + .collect(); + + if f.is_error() || arg_types.iter().any(|(_, t)| t.is_error()) { + return Some(Type::Error); + } + + match f { + Type::Function(params, ret) => { + let mut any_errors = false; + if params.len() != arg_types.len() { + // TODO: Augment with function name if known + self.report_error_tree(tree, format!("expected {} parameters", params.len())); + any_errors = true; + } + + for (i, ((t, a), p)) in arg_types.iter().zip(params.iter()).enumerate() { + if !a.compatible_with(p) { + self.report_error_tree_ref( + *t, + format!( + "parameter {i} has an incompatible type: expected {} but got {}", + p, a + ), + ); + any_errors = true; + } + } + + if any_errors { + return Some(Type::Error); + } + + Some(*ret.clone()) + } + _ => { + self.report_error_tree_ref(f_ref, format!("expected a function type, got: {f}")); + Some(Type::Error) + } + } } fn type_of_argument(&self, tree: &Tree) -> Option { assert_eq!(tree.kind, TreeKind::Argument); - Some(Type::Error) + + let result = self.type_of(tree.nth_tree(0)?); + Some(result) } fn type_of_expression_statement(&self, tree: &Tree) -> Option { @@ -785,13 +972,42 @@ impl<'a> Semantics<'a> { let id = tree.nth_token(0)?; let environment = self.environment_of(t); if let Some(declaration) = environment.bind(id) { - return Some(declaration.declaration_type); + return Some(match declaration { + Declaration::Variable { + declaration_type, .. + } => declaration_type.clone(), + Declaration::Function { + declaration_type, .. + } => declaration_type.clone(), + Declaration::ExternFunction { + declaration_type, .. + } => declaration_type.clone(), + }); } self.report_error_tree(tree, format!("cannot find value {id} here")); Some(Type::Error) } + fn type_of_function_decl(&self, tree: &Tree) -> Option { + let param_list = tree.child_tree_of_kind(self.syntax_tree, TreeKind::ParamList)?; + let mut parameter_types = Vec::new(); + for p in param_list.child_trees() { + let param = &self.syntax_tree[p]; + + // TODO: Missing type expression means it's a generic function. + let parameter_type = param.child_of_kind(self.syntax_tree, TreeKind::TypeExpression)?; + parameter_types.push(Box::new(self.type_of(parameter_type))); + } + + let return_type = match tree.child_tree_of_kind(self.syntax_tree, TreeKind::ReturnType) { + Some(t) => self.type_of(t.child_of_kind(self.syntax_tree, TreeKind::TypeExpression)?), + None => Type::Nothing, + }; + let return_type = Box::new(return_type); + Some(Type::Function(parameter_types, return_type)) + } + pub fn dump_compiler_state(&self, tr: Option) { eprintln!("Parsed the tree as:"); eprintln!("\n{}", self.syntax_tree.dump(true)); @@ -826,7 +1042,28 @@ impl<'a> Semantics<'a> { let mut environment = Some(self.environment_of(tr)); while let Some(env) = environment { for (k, v) in env.declarations.iter() { - eprintln!(" {k}: {:?}", v.declaration_type); + eprint!(" {k}: "); + match v { + Declaration::Variable { + declaration_type, + location, + index, + } => { + eprintln!("{declaration_type:?} (variable {location:?} {index})"); + } + Declaration::Function { + declaration_type, + declaration, + } => { + eprintln!("{declaration_type:?} (function {declaration:?})"); + } + Declaration::ExternFunction { + declaration_type, + id, + } => { + eprintln!("{declaration_type:?} (extern {id:?})"); + } + }; } environment = env.parent.clone(); } @@ -872,9 +1109,14 @@ pub fn check(s: &Semantics) { let _ = s.type_of(t); } TreeKind::ArgumentList => {} - TreeKind::Argument => {} + TreeKind::Argument => { + let _ = s.type_of(t); + } TreeKind::IfStatement => {} - TreeKind::Identifier => {} + TreeKind::Identifier => { + let _ = s.type_of(t); + } + TreeKind::ReturnType => {} } } } diff --git a/fine/tests/expression/errors/no_arg_pollution.fine b/fine/tests/expression/errors/no_arg_pollution.fine new file mode 100644 index 00000000..45b7fdad --- /dev/null +++ b/fine/tests/expression/errors/no_arg_pollution.fine @@ -0,0 +1,6 @@ +fun foo(x: f64) {} +x; + +// @type-error: 19 cannot find value x here +// Used to have a bug where statements after a function declaration would +// bind to the declaration's arguments, whoops. diff --git a/fine/tests/expression/errors/wrong_function.fine b/fine/tests/expression/errors/wrong_function.fine new file mode 100644 index 00000000..d5804df1 --- /dev/null +++ b/fine/tests/expression/errors/wrong_function.fine @@ -0,0 +1,5 @@ +fun foo(x: f64) {} +let x = foo("hello"); +x; + +// @type-error: 41 parameter 0 has an incompatible type: expected f64 but got string \ No newline at end of file diff --git a/fine/tests/expression/variable.fine b/fine/tests/expression/variable.fine index 0e23bc5c..ec2faf59 100644 --- a/fine/tests/expression/variable.fine +++ b/fine/tests/expression/variable.fine @@ -1,8 +1,9 @@ let x = 23; let y = x * 2; -y; +let z = print(y); +z; -// @type: 27 f64 +// @type: 41 f64 // @concrete: // | File // | LetStatement @@ -23,15 +24,29 @@ y; // | LiteralExpression // | Number:'"2"' // | Semicolon:'";"' +// | LetStatement +// | Let:'"let"' +// | Identifier:'"z"' +// | Equal:'"="' +// | CallExpression +// | Identifier +// | Identifier:'"print"' +// | ArgumentList +// | LeftParen:'"("' +// | Argument +// | Identifier +// | Identifier:'"y"' +// | RightParen:'")"' +// | Semicolon:'";"' // | ExpressionStatement // | Identifier -// | Identifier:'"y"' +// | Identifier:'"z"' // | Semicolon:'";"' // | // @compiles-to: // | function << module >> (0 args, 0 locals): // | strings (0): -// | code (8): +// | code (12): // | 0: PushFloat(23.0) // | 1: StoreModule(0) // | 2: LoadModule(0) @@ -39,5 +54,9 @@ y; // | 4: FloatMultiply // | 5: StoreModule(1) // | 6: LoadModule(1) -// | 7: Discard +// | 7: LoadExternFunction(0) +// | 8: Call(1) +// | 9: StoreModule(2) +// | 10: LoadModule(2) +// | 11: Discard // |