From e1afb6b43b18926de7393fe3e503749c6385a3de Mon Sep 17 00:00:00 2001 From: Jesse Brault Date: Tue, 3 Mar 2026 21:06:12 -0600 Subject: [PATCH] Much work on adding and calling. --- dmc-lib/src/asm/asm_instruction.rs | 40 ++++++ dmc-lib/src/ast/additive_expression.rs | 173 ++++++++++++++++++++++++ dmc-lib/src/ast/call.rs | 13 +- dmc-lib/src/ast/expression.rs | 28 ++++ dmc-lib/src/ast/expression_statement.rs | 3 + dmc-lib/src/ast/extern_function.rs | 11 +- dmc-lib/src/ast/function.rs | 24 +++- dmc-lib/src/ast/let_statement.rs | 7 + dmc-lib/src/ast/mod.rs | 1 + dmc-lib/src/ast/type_use.rs | 5 + dmc-lib/src/lexer.rs | 6 +- dmc-lib/src/parser.rs | 172 ++++++++++++++--------- dmc-lib/src/symbol.rs | 6 +- dmc-lib/src/token.rs | 2 + dmc-lib/src/type_info.rs | 22 +++ dvm-lib/src/instruction.rs | 5 + dvm-lib/src/vm/mod.rs | 28 ++++ dvm-lib/src/vm/value.rs | 16 +++ examples/add.dm | 5 + examples/call.dm | 9 ++ 20 files changed, 502 insertions(+), 74 deletions(-) create mode 100644 dmc-lib/src/ast/additive_expression.rs create mode 100644 examples/add.dm create mode 100644 examples/call.dm diff --git a/dmc-lib/src/asm/asm_instruction.rs b/dmc-lib/src/asm/asm_instruction.rs index a8d7da4..349c8c9 100644 --- a/dmc-lib/src/asm/asm_instruction.rs +++ b/dmc-lib/src/asm/asm_instruction.rs @@ -6,8 +6,10 @@ pub enum AsmInstruction { Move(Move), Push(Push), Pop(Pop), + InvokeStatic(InvokeStatic), InvokePlatformStatic(InvokePlatformStatic), LoadConstant(LoadConstant), + Add(Add), } impl AsmInstruction { @@ -16,10 +18,12 @@ impl AsmInstruction { AsmInstruction::Move(asm_move) => asm_move.dvm(), AsmInstruction::Push(push) => push.dvm(), AsmInstruction::Pop(pop) => pop.dvm(), + AsmInstruction::InvokeStatic(invoke_static) => invoke_static.dvm(), AsmInstruction::InvokePlatformStatic(invoke_platform_static) => { invoke_platform_static.dvm() } AsmInstruction::LoadConstant(load_constant) => load_constant.dvm(), + AsmInstruction::Add(add) => add.dvm(), } } } @@ -100,6 +104,21 @@ impl Pop { } } +#[derive(Debug)] +pub struct InvokeStatic { + name: String, +} + +impl InvokeStatic { + pub fn new(name: &str) -> Self { + Self { name: name.into() } + } + + pub fn dvm(&self) -> Instruction { + Instruction::InvokeStatic(Rc::from(self.name.clone())) + } +} + #[derive(Debug)] pub struct InvokePlatformStatic { name: String, @@ -137,3 +156,24 @@ impl LoadConstant { Instruction::LoadStringConstant(Rc::from(self.name.clone()), self.destination_register) } } + +#[derive(Debug)] +pub enum Add { + IntInt(usize, usize, usize), + StringString(usize, usize, usize), + StringInt(usize, usize, usize), +} + +impl Add { + pub fn dvm(&self) -> Instruction { + match self { + Add::IntInt(lhs, rhs, destination) => Instruction::AddIntInt(*lhs, *rhs, *destination), + Add::StringString(lhs, rhs, destination) => { + Instruction::AddStringString(*lhs, *rhs, *destination) + } + Add::StringInt(lhs, rhs, destination) => { + Instruction::AddStringInt(*lhs, *rhs, *destination) + } + } + } +} diff --git a/dmc-lib/src/ast/additive_expression.rs b/dmc-lib/src/ast/additive_expression.rs new file mode 100644 index 0000000..e765a96 --- /dev/null +++ b/dmc-lib/src/ast/additive_expression.rs @@ -0,0 +1,173 @@ +use crate::asm::asm_instruction::{Add, AsmInstruction, LoadConstant, Move, Operand, Pop}; +use crate::ast::assemble_context::AssembleContext; +use crate::ast::expression::Expression; +use crate::constants_table::ConstantsTable; +use crate::diagnostic::Diagnostic; +use crate::source_range::SourceRange; +use crate::symbol::ExpressibleSymbol; +use crate::symbol_table::SymbolTable; +use crate::type_info::TypeInfo; + +pub struct AdditiveExpression { + lhs: Box, + rhs: Box, + source_range: SourceRange, +} + +impl AdditiveExpression { + pub fn new(lhs: Expression, rhs: Expression, source_range: SourceRange) -> Self { + Self { + lhs: lhs.into(), + rhs: rhs.into(), + source_range, + } + } + + pub fn gather_declared_names( + &mut self, + symbol_table: &mut SymbolTable, + ) -> Result<(), Vec> { + let mut diagnostics = vec![]; + diagnostics.append(&mut self.lhs.gather_declared_names(symbol_table)); + diagnostics.append(&mut self.rhs.gather_declared_names(symbol_table)); + if diagnostics.is_empty() { + Ok(()) + } else { + Err(diagnostics) + } + } + + pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { + let mut diagnostics = vec![]; + diagnostics.append(&mut self.lhs.check_name_usages(symbol_table)); + diagnostics.append(&mut self.rhs.check_name_usages(symbol_table)); + if diagnostics.is_empty() { + Ok(()) + } else { + Err(diagnostics) + } + } + + pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Result<(), Vec> { + self.lhs.type_check(symbol_table); + self.rhs.type_check(symbol_table); + + let lhs_type_info = self.lhs.type_info(); + let rhs_type_info = self.rhs.type_info(); + if lhs_type_info.can_add(&rhs_type_info) { + Ok(()) + } else { + Err(vec![Diagnostic::new( + &format!("Cannot add {} to {}", lhs_type_info, rhs_type_info), + self.source_range.start(), + self.source_range.end(), + )]) + } + } + + fn assemble_side( + expression: &Expression, + context: &mut AssembleContext, + symbol_table: &SymbolTable, + constants_table: &mut ConstantsTable, + ) -> usize { + match expression { + Expression::Call(call) => { + call.assemble(context, symbol_table, constants_table); + let register = context.new_local_register(); + context.instruction(AsmInstruction::Pop(Pop::new(register))); + register + } + Expression::IntegerLiteral(integer_literal) => { + let register = context.new_local_register(); + context.instruction(AsmInstruction::Move(Move::new( + Operand::IntegerLiteral(integer_literal.value()), + register, + ))); + register + } + Expression::String(string_literal) => { + let register = context.new_local_register(); + let constant_name = constants_table.insert_string(string_literal.content()); + context.instruction(AsmInstruction::LoadConstant(LoadConstant::new( + &constant_name, + register, + ))); + register + } + Expression::Identifier(identifier) => { + let register = context.new_local_register(); + match identifier.expressible_symbol() { + ExpressibleSymbol::Function(_) => unreachable!(), + ExpressibleSymbol::Parameter(parameter_symbol) => { + let offset = parameter_symbol.borrow().stack_frame_offset(); + context.instruction(AsmInstruction::Move(Move::new( + Operand::StackFrameOffset(offset), + register, + ))); + } + ExpressibleSymbol::Variable(variable_symbol) => { + context.instruction(AsmInstruction::Move(Move::new( + Operand::Register(variable_symbol.borrow().register()), + register, + ))); + } + } + register + } + Expression::Additive(additive_expression) => { + additive_expression.assemble(context, symbol_table, constants_table) + } + } + } + + pub fn assemble( + &self, + context: &mut AssembleContext, + symbol_table: &SymbolTable, + constants_table: &mut ConstantsTable, + ) -> usize { + let lhs_register = Self::assemble_side(&self.lhs, context, symbol_table, constants_table); + let rhs_register = Self::assemble_side(&self.rhs, context, symbol_table, constants_table); + let result_register = context.new_local_register(); + match self.lhs.type_info() { + TypeInfo::Integer => match self.rhs.type_info() { + TypeInfo::Integer => { + context.instruction(AsmInstruction::Add(Add::IntInt( + lhs_register, + rhs_register, + result_register, + ))); + } + _ => unreachable!(), + }, + TypeInfo::String => match self.rhs.type_info() { + TypeInfo::Integer => { + context.instruction(AsmInstruction::Add(Add::StringInt( + lhs_register, + rhs_register, + result_register, + ))); + } + TypeInfo::String => { + context.instruction(AsmInstruction::Add(Add::StringString( + lhs_register, + rhs_register, + result_register, + ))); + } + _ => unreachable!(), + }, + _ => unreachable!(), + } + result_register + } + + pub fn type_info(&self) -> TypeInfo { + self.lhs.type_info().additive_result(&self.rhs.type_info()) + } + + pub fn source_range(&self) -> &SourceRange { + &self.source_range + } +} diff --git a/dmc-lib/src/ast/call.rs b/dmc-lib/src/ast/call.rs index 9c6f698..0d59975 100644 --- a/dmc-lib/src/ast/call.rs +++ b/dmc-lib/src/ast/call.rs @@ -1,5 +1,5 @@ use crate::asm::asm_instruction::{ - AsmInstruction, InvokePlatformStatic, LoadConstant, Operand, Push, + AsmInstruction, InvokePlatformStatic, InvokeStatic, LoadConstant, Operand, Push, }; use crate::ast::assemble_context::AssembleContext; use crate::ast::expression::Expression; @@ -166,6 +166,13 @@ impl Call { )))) } }, + Expression::Additive(additive_expression) => { + let result_register = + additive_expression.assemble(context, symbol_table, constants_table); + context.instruction(AsmInstruction::Push(Push::new(Operand::Register( + result_register, + )))); + } } } @@ -187,7 +194,9 @@ impl Call { InvokePlatformStatic::new(function_symbol.name(), arg_count), )); } else { - todo!("non-platform invoke") + context.instruction(AsmInstruction::InvokeStatic(InvokeStatic::new( + function_symbol.name(), + ))); } } diff --git a/dmc-lib/src/ast/expression.rs b/dmc-lib/src/ast/expression.rs index 8d9dbba..038685c 100644 --- a/dmc-lib/src/ast/expression.rs +++ b/dmc-lib/src/ast/expression.rs @@ -1,3 +1,4 @@ +use crate::ast::additive_expression::AdditiveExpression; use crate::ast::call::Call; use crate::ast::identifier::Identifier; use crate::ast::integer_literal::IntegerLiteral; @@ -12,6 +13,7 @@ pub enum Expression { IntegerLiteral(IntegerLiteral), String(StringLiteral), Identifier(Identifier), + Additive(AdditiveExpression), } impl Expression { @@ -19,6 +21,14 @@ impl Expression { match self { Expression::Call(call) => call.gather_declared_names(symbol_table), Expression::Identifier(identifier) => identifier.gather_declared_names(symbol_table), + Expression::Additive(additive_expression) => { + match additive_expression.gather_declared_names(symbol_table) { + Ok(_) => { + vec![] + } + Err(diagnostics) => diagnostics, + } + } _ => vec![], } } @@ -27,6 +37,14 @@ impl Expression { match self { Expression::Call(call) => call.check_name_usages(symbol_table), Expression::Identifier(identifier) => identifier.check_name_usages(symbol_table), + Expression::Additive(additive_expression) => { + match additive_expression.check_name_usages(symbol_table) { + Ok(_) => { + vec![] + } + Err(diagnostics) => diagnostics, + } + } _ => vec![], } } @@ -34,6 +52,14 @@ impl Expression { pub fn type_check(&mut self, symbol_table: &SymbolTable) -> Vec { match self { Expression::Call(call) => call.type_check(symbol_table), + Expression::Additive(additive_expression) => { + match additive_expression.type_check(symbol_table) { + Ok(_) => { + vec![] + } + Err(diagnostics) => diagnostics, + } + } _ => vec![], } } @@ -44,6 +70,7 @@ impl Expression { Expression::IntegerLiteral(_) => TypeInfo::Integer, Expression::String(_) => TypeInfo::String, Expression::Identifier(identifier) => identifier.type_info(), + Expression::Additive(additive_expression) => additive_expression.type_info(), } } @@ -53,6 +80,7 @@ impl Expression { Expression::IntegerLiteral(integer_literal) => integer_literal.source_range(), Expression::String(string_literal) => string_literal.source_range(), Expression::Identifier(identifier) => identifier.source_range(), + Expression::Additive(additive_expression) => additive_expression.source_range(), } } } diff --git a/dmc-lib/src/ast/expression_statement.rs b/dmc-lib/src/ast/expression_statement.rs index 35b4cd5..79ddd67 100644 --- a/dmc-lib/src/ast/expression_statement.rs +++ b/dmc-lib/src/ast/expression_statement.rs @@ -41,6 +41,9 @@ impl ExpressionStatement { Expression::Call(call) => { call.assemble(context, symbol_table, constants_table); } + Expression::Additive(additive) => { + additive.assemble(context, symbol_table, constants_table); + } _ => unreachable!(), } } diff --git a/dmc-lib/src/ast/extern_function.rs b/dmc-lib/src/ast/extern_function.rs index 7efa5fb..f52d00a 100644 --- a/dmc-lib/src/ast/extern_function.rs +++ b/dmc-lib/src/ast/extern_function.rs @@ -1,4 +1,5 @@ use crate::ast::parameter::Parameter; +use crate::ast::type_use::TypeUse; use crate::diagnostic::Diagnostic; use crate::source_range::SourceRange; use crate::symbol::FunctionSymbol; @@ -8,6 +9,7 @@ pub struct ExternFunction { declared_name: String, declared_name_source_range: SourceRange, parameters: Vec, + return_type: TypeUse, } impl ExternFunction { @@ -15,11 +17,13 @@ impl ExternFunction { name: &str, declared_name_source_range: SourceRange, parameters: Vec, + return_type: TypeUse, ) -> ExternFunction { ExternFunction { declared_name: name.into(), declared_name_source_range, parameters, + return_type, } } @@ -30,8 +34,11 @@ impl ExternFunction { pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec { let mut diagnostics = vec![]; - let insert_result = - symbol_table.insert_function_symbol(FunctionSymbol::new(&self.declared_name, true)); + let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new( + &self.declared_name, + true, + self.return_type.to_type_info(), + )); let function_symbol = match insert_result { Ok(function_symbol) => function_symbol, diff --git a/dmc-lib/src/ast/function.rs b/dmc-lib/src/ast/function.rs index 2da4eb5..43020ad 100644 --- a/dmc-lib/src/ast/function.rs +++ b/dmc-lib/src/ast/function.rs @@ -1,16 +1,20 @@ use crate::ast::assemble_context::AssembleContext; use crate::ast::parameter::Parameter; use crate::ast::statement::Statement; +use crate::ast::type_use::TypeUse; use crate::constants_table::ConstantsTable; use crate::diagnostic::Diagnostic; use crate::source_range::SourceRange; use crate::symbol::FunctionSymbol; use crate::symbol_table::{SymbolInsertError, SymbolTable}; +use crate::type_info::TypeInfo; +use std::ops::Neg; pub struct Function { declared_name: String, declared_name_source_range: SourceRange, parameters: Vec, + return_type: Option, statements: Vec, } @@ -19,12 +23,14 @@ impl Function { declared_name: &str, declared_name_source_range: SourceRange, parameters: Vec, + return_type: Option, statements: Vec, ) -> Self { Self { declared_name: declared_name.to_string(), declared_name_source_range, parameters, + return_type, statements, } } @@ -41,8 +47,14 @@ impl Function { let mut diagnostics = vec![]; // insert function symbol - let insert_result = - symbol_table.insert_function_symbol(FunctionSymbol::new(self.declared_name(), false)); + let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new( + self.declared_name(), + false, + self.return_type + .as_ref() + .map(|return_type| return_type.to_type_info()) + .unwrap_or(TypeInfo::Void), + )); // get function symbol if successful let function_symbol = match insert_result { @@ -67,9 +79,14 @@ impl Function { // handle parameters symbol_table.push_scope(&format!("parameter_scope({})", self.declared_name)); let mut parameter_symbols = vec![]; - for parameter in &mut self.parameters { + let parameter_count = self.parameters.len(); + let stack_frame_offset_base = (parameter_count as isize).neg(); + for (i, parameter) in self.parameters.iter_mut().enumerate() { match parameter.gather_declared_names(symbol_table) { Ok(parameter_symbol) => { + parameter_symbol + .borrow_mut() + .set_stack_frame_offset(stack_frame_offset_base + (i as isize)); parameter_symbols.push(parameter_symbol); } Err(mut parameter_diagnostics) => { @@ -144,6 +161,7 @@ impl Function { for statement in &self.statements { statement.assemble(context, symbol_table, constants_table); } + // todo: function exit, including popping passed args and pushing return value context.complete_function(); } } diff --git a/dmc-lib/src/ast/let_statement.rs b/dmc-lib/src/ast/let_statement.rs index 9c18b3c..4cccf68 100644 --- a/dmc-lib/src/ast/let_statement.rs +++ b/dmc-lib/src/ast/let_statement.rs @@ -130,6 +130,13 @@ impl LetStatement { } } } + Expression::Additive(additive) => { + let result_register = additive.assemble(context, symbol_table, constants_table); + context.instruction(AsmInstruction::Move(Move::new( + Operand::Register(result_register), + destination_register, + ))); + } } } } diff --git a/dmc-lib/src/ast/mod.rs b/dmc-lib/src/ast/mod.rs index 818a9d9..83f5ba5 100644 --- a/dmc-lib/src/ast/mod.rs +++ b/dmc-lib/src/ast/mod.rs @@ -1,3 +1,4 @@ +pub mod additive_expression; pub mod assemble_context; pub mod call; pub mod compilation_unit; diff --git a/dmc-lib/src/ast/type_use.rs b/dmc-lib/src/ast/type_use.rs index f25058e..e72393a 100644 --- a/dmc-lib/src/ast/type_use.rs +++ b/dmc-lib/src/ast/type_use.rs @@ -1,4 +1,5 @@ use crate::source_range::SourceRange; +use crate::type_info::TypeInfo; pub struct TypeUse { declared_name: String, @@ -16,4 +17,8 @@ impl TypeUse { pub fn declared_name(&self) -> &str { &self.declared_name } + + pub fn to_type_info(&self) -> TypeInfo { + TypeInfo::from_declared_name(self.declared_name()) + } } diff --git a/dmc-lib/src/lexer.rs b/dmc-lib/src/lexer.rs index 0254796..7acaa43 100644 --- a/dmc-lib/src/lexer.rs +++ b/dmc-lib/src/lexer.rs @@ -34,7 +34,9 @@ impl<'a> Lexer<'a> { } } - let token = if chunk.starts_with("(") { + let token = if chunk.starts_with("->") { + Token::new(self.position, self.position + 2, TokenKind::RightArrow) + } else if chunk.starts_with("(") { Token::new(self.position, self.position + 1, TokenKind::LeftParentheses) } else if chunk.starts_with(")") { Token::new( @@ -42,6 +44,8 @@ impl<'a> Lexer<'a> { self.position + 1, TokenKind::RightParentheses, ) + } else if chunk.starts_with("+") { + Token::new(self.position, self.position + 1, TokenKind::Plus) } else if chunk.starts_with("=") { Token::new(self.position, self.position + 1, TokenKind::Equals) } else if chunk.starts_with(",") { diff --git a/dmc-lib/src/parser.rs b/dmc-lib/src/parser.rs index 98413a1..d796847 100644 --- a/dmc-lib/src/parser.rs +++ b/dmc-lib/src/parser.rs @@ -1,3 +1,4 @@ +use crate::ast::additive_expression::AdditiveExpression; use crate::ast::call::Call; use crate::ast::compilation_unit::CompilationUnit; use crate::ast::expression::Expression; @@ -218,13 +219,26 @@ impl<'a> Parser<'a> { fn function(&mut self) -> Result> { self.expect_advance(TokenKind::Fn)?; let identifier_token = self.expect_advance(TokenKind::Identifier)?; + self.expect_advance(TokenKind::LeftParentheses)?; - let parameters = self.parameter_list()?; - self.expect_advance(TokenKind::RightParentheses)?; - let mut statements = vec![]; + let mut diagnostics = vec![]; + + let return_type = if self.current.is_some() && self.peek_current(TokenKind::RightArrow) { + match self.return_type() { + Ok(type_use) => Some(type_use), + Err(mut return_type_diagnostics) => { + diagnostics.append(&mut return_type_diagnostics); + None + } + } + } else { + None + }; + + let mut statements = vec![]; while self.current.is_some() && !self.peek_current(TokenKind::End) { let statement_result = self.statement(); match statement_result { @@ -251,6 +265,7 @@ impl<'a> Parser<'a> { self.token_text(&identifier_token), SourceRange::new(identifier_token.start(), identifier_token.end()), parameters, + return_type, statements, )) } else { @@ -285,11 +300,14 @@ impl<'a> Parser<'a> { Ok(_) => {} } + let return_type = self.return_type()?; + if diagnostics.is_empty() { Ok(ExternFunction::new( self.token_text(&identifier_token), SourceRange::new(identifier_token.start(), identifier_token.end()), maybe_parameters.unwrap(), + return_type, )) } else { Err(diagnostics) @@ -331,6 +349,11 @@ impl<'a> Parser<'a> { )) } + fn return_type(&mut self) -> Result> { + self.expect_advance(TokenKind::RightArrow)?; + self.type_use() + } + fn type_use(&mut self) -> Result> { let identifier_token = self.expect_advance(TokenKind::Identifier)?; Ok(TypeUse::new( @@ -364,96 +387,102 @@ impl<'a> Parser<'a> { } fn expression(&mut self) -> Result> { - let current = self.get_current().clone(); // I don't love this clone - let mut diagnostics = vec![]; - let mut expression = match current.kind() { + self.additive_expression() + } + + fn additive_expression(&mut self) -> Result> { + let mut result = self.suffix_expression()?; + while self.current.is_some() { + let current = self.get_current(); + match current.kind() { + TokenKind::Plus => { + self.advance(); // plus + let rhs = self.expression()?; + let source_range = + SourceRange::new(result.source_range().start(), rhs.source_range().end()); + result = + Expression::Additive(AdditiveExpression::new(result, rhs, source_range)); + } + _ => break, + } + } + Ok(result) + } + + fn suffix_expression(&mut self) -> Result> { + let mut result = self.expression_base()?; + while self.current.is_some() { + let current = self.get_current(); + match current.kind() { + TokenKind::LeftParentheses => { + result = Expression::Call(self.call(result)?); + } + _ => break, + } + } + Ok(result) + } + + fn expression_base(&mut self) -> Result> { + let current = self.get_current(); + match current.kind() { TokenKind::IntegerLiteral => { let raw = self.token_text(¤t); let source_range = SourceRange::new(current.start(), current.end()); self.advance(); - Expression::IntegerLiteral(IntegerLiteral::new( + Ok(Expression::IntegerLiteral(IntegerLiteral::new( i32::from_str(raw).unwrap(), source_range, - )) + ))) } TokenKind::String => { let with_quotes = self.token_text(¤t); let source_range = SourceRange::new(current.start(), current.end()); self.advance(); - Expression::String(StringLiteral::new( + Ok(Expression::String(StringLiteral::new( &with_quotes[1..with_quotes.len() - 1], source_range, - )) + ))) } TokenKind::Identifier => { let declared_name = self.token_text(¤t); let source_range = SourceRange::new(current.start(), current.end()); self.advance(); - Expression::Identifier(Identifier::new(declared_name, source_range)) + Ok(Expression::Identifier(Identifier::new( + declared_name, + source_range, + ))) } - _ => { - diagnostics.push(Diagnostic::new( - &format!( - "Expected any of {:?} but found {:?}", - [ - TokenKind::IntegerLiteral, - TokenKind::String, - TokenKind::Identifier - ], - current.kind() - ), - current.start(), - current.end(), - )); - - self.advance_until(&[ - TokenKind::IntegerLiteral, - TokenKind::String, - TokenKind::Identifier, - ]); - if self.current.is_some() { - let try_again_result = self.expression(); - match try_again_result { - Ok(expression) => expression, - Err(mut try_again_diagnostics) => { - diagnostics.append(&mut try_again_diagnostics); - return Err(diagnostics); - } - } - } else { - return Err(diagnostics); - } - } - }; - - // postfixes - while let Some(current) = &self.current { - match current.kind() { - TokenKind::LeftParentheses => { - expression = Expression::Call(self.call(expression)?); - } - _ => break, - } - } - - if diagnostics.is_empty() { - Ok(expression) - } else { - Err(diagnostics) + _ => unreachable!(), } } fn call(&mut self, callee: Expression) -> Result> { self.expect_advance(TokenKind::LeftParentheses)?; let mut arguments = vec![]; - while self.current.is_some() && !self.peek_current(TokenKind::RightParentheses) { - arguments.push(self.expression()?); + if let Some(current) = &self.current { + if matches!( + current.kind(), + TokenKind::IntegerLiteral | TokenKind::String | TokenKind::Identifier + ) { + arguments.append(&mut self.expression_list()?); + } } let right_parentheses_token = self.expect_advance(TokenKind::RightParentheses)?; let source_range = SourceRange::new(callee.source_range().start(), right_parentheses_token.end()); Ok(Call::new(callee, arguments, source_range)) } + + fn expression_list(&mut self) -> Result, Vec> { + let mut expressions = vec![]; + expressions.push(self.expression()?); + while self.current.is_some() && self.peek_current(TokenKind::Comma) { + self.advance(); // comma + expressions.push(self.expression()?); + } + Ok(expressions) + } } #[cfg(test)] @@ -483,7 +512,7 @@ mod smoke_tests { #[test] fn extern_fn_with_param() { - smoke_test("extern fn println(message: Any)"); + smoke_test("extern fn println(message: Any) -> Void"); } #[test] @@ -495,6 +524,21 @@ mod smoke_tests { fn fn_with_params() { smoke_test("fn foo(bar: Int, baz: Int) end"); } + + #[test] + fn return_type() { + smoke_test("fn foo() -> Int end") + } + + #[test] + fn extern_return_type() { + smoke_test("extern fn foo() -> Int"); + } + + #[test] + fn add_two_numbers() { + smoke_test("fn main() 1 + 2 end"); + } } #[cfg(test)] @@ -503,7 +547,7 @@ mod concrete_tests { #[test] fn parses_extern_fn() { - let parse_result = parse_compilation_unit("extern fn println()"); + let parse_result = parse_compilation_unit("extern fn println() -> Void"); let compilation_unit = match parse_result { Ok(compilation_unit) => compilation_unit, Err(diagnostics) => { diff --git a/dmc-lib/src/symbol.rs b/dmc-lib/src/symbol.rs index f5c53eb..1f990ed 100644 --- a/dmc-lib/src/symbol.rs +++ b/dmc-lib/src/symbol.rs @@ -6,14 +6,16 @@ pub struct FunctionSymbol { name: Rc, is_platform: bool, parameters: Option>>>, + return_type: TypeInfo, } impl FunctionSymbol { - pub fn new(name: &str, is_platform: bool) -> Self { + pub fn new(name: &str, is_platform: bool, return_type: TypeInfo) -> Self { Self { name: name.into(), is_platform, parameters: None, + return_type, } } @@ -34,7 +36,7 @@ impl FunctionSymbol { } pub fn return_type(&self) -> TypeInfo { - todo!() + self.return_type.clone() } pub fn is_platform(&self) -> bool { diff --git a/dmc-lib/src/token.rs b/dmc-lib/src/token.rs index 29751e3..531585e 100644 --- a/dmc-lib/src/token.rs +++ b/dmc-lib/src/token.rs @@ -38,4 +38,6 @@ pub enum TokenKind { Extern, Comma, Colon, + RightArrow, + Plus, } diff --git a/dmc-lib/src/type_info.rs b/dmc-lib/src/type_info.rs index 8456562..db54769 100644 --- a/dmc-lib/src/type_info.rs +++ b/dmc-lib/src/type_info.rs @@ -9,6 +9,7 @@ pub enum TypeInfo { Integer, String, Function(Rc>), + Void, } impl Display for TypeInfo { @@ -24,6 +25,7 @@ impl Display for TypeInfo { } write!(f, ")") } + TypeInfo::Void => write!(f, "Void"), } } } @@ -35,6 +37,7 @@ impl TypeInfo { "Any" => TypeInfo::Any, "Int" => TypeInfo::Integer, "String" => TypeInfo::String, + "Void" => TypeInfo::Void, _ => panic!("Unknown type: {}", declared_name), } } @@ -51,6 +54,25 @@ impl TypeInfo { TypeInfo::Function(_) => { unimplemented!("Type matching on Functions not yet supported.") } + TypeInfo::Void => { + matches!(other, TypeInfo::Void) + } + } + } + + pub fn can_add(&self, rhs: &Self) -> bool { + match self { + TypeInfo::Integer => matches!(rhs, TypeInfo::Integer), + TypeInfo::String => matches!(rhs, TypeInfo::String | TypeInfo::Integer), + _ => false, + } + } + + pub fn additive_result(&self, _rhs: &Self) -> TypeInfo { + match self { + TypeInfo::Integer => TypeInfo::Integer, + TypeInfo::String => TypeInfo::String, + _ => panic!("Adding things other than integers and strings not yet supported"), } } } diff --git a/dvm-lib/src/instruction.rs b/dvm-lib/src/instruction.rs index e9deecf..abe6ad2 100644 --- a/dvm-lib/src/instruction.rs +++ b/dvm-lib/src/instruction.rs @@ -14,9 +14,14 @@ pub enum Instruction { PushInt(i32), PushStackFrameOffset(isize), + InvokeStatic(FunctionName), InvokePlatformStatic(FunctionName, ArgCount), LoadStringConstant(ConstantName, Register), Pop(Option), + + AddIntInt(Register, Register, Register), + AddStringInt(Register, Register, Register), + AddStringString(Register, Register, Register), } diff --git a/dvm-lib/src/vm/mod.rs b/dvm-lib/src/vm/mod.rs index e37cfb0..a0c8fa9 100644 --- a/dvm-lib/src/vm/mod.rs +++ b/dvm-lib/src/vm/mod.rs @@ -174,6 +174,14 @@ pub fn call( } /* Invoke instructions */ + Instruction::InvokeStatic(function_name) => { + let function = context + .functions + .get(function_name) + .expect(&format!("Function {} not found", function_name)); + todo!("Call stack for invoking Deimos-native functions") + } + Instruction::InvokePlatformStatic(function_name, arg_count) => { let stack = state.stack(); let args = &stack[stack.len() - arg_count..]; @@ -210,6 +218,26 @@ pub fn call( } } + /* Add instructions */ + Instruction::AddIntInt(lhs, rhs, destination) => { + let lhs_value = state.registers()[*lhs].unwrap_int(); + let rhs_value = state.registers()[*rhs].unwrap_int(); + let result = lhs_value + rhs_value; + state.registers_mut()[*destination] = Value::Int(result); + } + Instruction::AddStringInt(lhs, rhs, destination) => { + let lhs_value = state.registers()[*lhs].unwrap_string(); + let rhs_value = state.registers()[*rhs].unwrap_int(); + let formatted = format!("{}{}", lhs_value, rhs_value); + state.registers_mut()[*destination] = Value::String(Rc::from(formatted)); + } + Instruction::AddStringString(lhs, rhs, destination) => { + let lhs_value = state.registers()[*lhs].unwrap_string(); + let rhs_value = state.registers()[*rhs].unwrap_string(); + let formatted = format!("{}{}", lhs_value, rhs_value); + state.registers_mut()[*destination] = Value::String(Rc::from(formatted)); + } + /* Pop instructions */ Instruction::Pop(maybe_register) => { let value = state.stack_mut().pop().unwrap(); diff --git a/dvm-lib/src/vm/value.rs b/dvm-lib/src/vm/value.rs index 9c55ae0..f2c63bf 100644 --- a/dvm-lib/src/vm/value.rs +++ b/dvm-lib/src/vm/value.rs @@ -12,3 +12,19 @@ impl Default for Value { Value::Null } } + +impl Value { + pub fn unwrap_int(&self) -> i32 { + match self { + Value::Int(i) => *i, + _ => panic!(), + } + } + + pub fn unwrap_string(&self) -> &Rc { + match self { + Value::String(s) => s, + _ => panic!(), + } + } +} diff --git a/examples/add.dm b/examples/add.dm new file mode 100644 index 0000000..f381118 --- /dev/null +++ b/examples/add.dm @@ -0,0 +1,5 @@ +extern fn println(message: Any) -> Void + +fn main() + println(6 + 7) +end diff --git a/examples/call.dm b/examples/call.dm new file mode 100644 index 0000000..81e0478 --- /dev/null +++ b/examples/call.dm @@ -0,0 +1,9 @@ +extern fn println(message: Any) -> Void + +fn add(a: Int, b: Int) -> Int + a + b +end + +fn main() + println(add(1, 2)) +end \ No newline at end of file