diff --git a/Cargo.lock b/Cargo.lock index e2d9d37..6722a0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -221,10 +221,18 @@ version = "0.1.0" dependencies = [ "clap", "codespan-reporting 0.13.1", + "dm-std-lib", "dmc-lib", "dvm-lib", ] +[[package]] +name = "dm-std-lib" +version = "0.1.0" +dependencies = [ + "dvm-lib", +] + [[package]] name = "dmc-lib" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 08f07ab..4a95eea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2021" #[[bin]] #name = "dm" -#path = "src/bin/dvm/main.rs" +#path = "src/bin/dvm/lib" [[bin]] name = "dmc" @@ -25,4 +25,4 @@ cst-test-generator = { path = "cst-test-generator" } [workspace] resolver = "3" -members = ["ast-generator", "cst-test-generator", "dm", "dmc-lib", "dvm-lib"] +members = ["ast-generator", "cst-test-generator", "dm", "dm-std-lib", "dmc-lib", "dvm-lib"] diff --git a/dm-std-lib/Cargo.toml b/dm-std-lib/Cargo.toml new file mode 100644 index 0000000..c3e8d65 --- /dev/null +++ b/dm-std-lib/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "dm-std-lib" +version = "0.1.0" +edition = "2024" + +[dependencies] +dvm-lib = { path = "../dvm-lib" } \ No newline at end of file diff --git a/dm-std-lib/src/lib.rs b/dm-std-lib/src/lib.rs new file mode 100644 index 0000000..f7e2fc2 --- /dev/null +++ b/dm-std-lib/src/lib.rs @@ -0,0 +1,61 @@ +use dvm_lib::vm::value::Value; +use dvm_lib::vm::{DvmContext, DvmState}; +use std::error::Error; +use std::fmt::{Debug, Display, Formatter}; +use std::rc::Rc; + +pub fn add_all_std_core(context: &mut DvmContext) { + let platform_functions = context.platform_functions_mut(); + platform_functions.insert(Rc::from("println"), std_core_println); +} + +struct StdCoreError { + message: String, +} + +impl StdCoreError { + pub fn new(message: &str) -> StdCoreError { + Self { + message: message.into(), + } + } +} + +impl Debug for StdCoreError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + todo!() + } +} + +impl Display for StdCoreError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.message) + } +} + +impl Error for StdCoreError {} + +pub fn std_core_println( + context: &DvmContext, + state: &DvmState, + args: &[Value], +) -> Result> { + let maybe_to_print = args.get(0); + match maybe_to_print { + None => Err(Box::new(StdCoreError::new("Missing to_print arg"))), + Some(to_print) => { + match to_print { + Value::Int(i) => { + println!("{}", i); + } + Value::String(s) => { + println!("{}", s); + } + Value::Null => { + println!("null"); + } + } + Ok(Value::Null) + } + } +} diff --git a/dm/Cargo.toml b/dm/Cargo.toml index e70abb9..8c48197 100644 --- a/dm/Cargo.toml +++ b/dm/Cargo.toml @@ -6,5 +6,6 @@ edition = "2024" [dependencies] dmc-lib = { path = "../dmc-lib" } dvm-lib = { path = "../dvm-lib" } +dm-std-lib = { path = "../dm-std-lib" } clap = { version = "4.5.60", features = ["derive"] } codespan-reporting = "0.13.1" \ No newline at end of file diff --git a/dm/src/main.rs b/dm/src/main.rs index 6bb3716..4907871 100644 --- a/dm/src/main.rs +++ b/dm/src/main.rs @@ -3,6 +3,7 @@ use codespan_reporting::diagnostic::Label; use codespan_reporting::files::SimpleFiles; use codespan_reporting::term; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; +use dm_std_lib::add_all_std_core; use dmc_lib::constants_table::ConstantsTable; use dmc_lib::diagnostic::Diagnostic; use dmc_lib::parser::parse_compilation_unit; @@ -59,6 +60,9 @@ fn main() { let mut dvm_context = DvmContext::new(); + // add std::core fns + add_all_std_core(&mut dvm_context); + for asm_function in &asm_functions { let function = asm_function.dvm(); dvm_context.add_function(function); diff --git a/dmc-lib/src/asm/asm_instruction.rs b/dmc-lib/src/asm/asm_instruction.rs index a3706a3..a8d7da4 100644 --- a/dmc-lib/src/asm/asm_instruction.rs +++ b/dmc-lib/src/asm/asm_instruction.rs @@ -103,15 +103,19 @@ impl Pop { #[derive(Debug)] pub struct InvokePlatformStatic { name: String, + arg_count: usize, } impl InvokePlatformStatic { - pub fn new(name: &str) -> Self { - Self { name: name.into() } + pub fn new(name: &str, arg_count: usize) -> Self { + Self { + name: name.into(), + arg_count, + } } pub fn dvm(&self) -> Instruction { - Instruction::InvokePlatformStatic(Rc::from(self.name.clone())) + Instruction::InvokePlatformStatic(Rc::from(self.name.clone()), self.arg_count) } } diff --git a/dmc-lib/src/ast/call.rs b/dmc-lib/src/ast/call.rs index aeb95d5..b578509 100644 --- a/dmc-lib/src/ast/call.rs +++ b/dmc-lib/src/ast/call.rs @@ -62,14 +62,51 @@ impl Call { } // check that callee is callable - match self.callee.type_info() { - TypeInfo::Function(_) => {} + let function_symbol = match self.callee.type_info() { + TypeInfo::Function(function_symbol) => function_symbol, _ => { diagnostics.push(Diagnostic::new( "Receiver is not callable", self.callee.source_range().start(), self.callee.source_range().end(), )); + return diagnostics; + } + }; + + // check arguments length + let function_symbol_ref = function_symbol.borrow(); + let parameters = function_symbol_ref.parameters(); + if parameters.len() != self.arguments.len() { + diagnostics.push(Diagnostic::new( + &format!( + "Wrong number of arguments; expected {} but found {}", + parameters.len(), + self.arguments.len() + ), + self.source_range.start(), + self.source_range.end(), + )); + } + + if !diagnostics.is_empty() { + return diagnostics; + } + + // check argument types + for i in 0..parameters.len() { + let parameter = ¶meters[i]; + let argument = &self.arguments[i]; + if parameter.borrow().type_info() != &argument.type_info() { + diagnostics.push(Diagnostic::new( + &format!( + "Mismatched types; expected {} but found {}", + parameter.borrow().type_info(), + argument.type_info() + ), + argument.source_range().start(), + argument.source_range().end(), + )) } } @@ -78,7 +115,7 @@ impl Call { pub fn type_info(&self) -> TypeInfo { match self.callee.type_info() { - TypeInfo::Function(function_symbol) => function_symbol.return_type(), + TypeInfo::Function(function_symbol) => function_symbol.borrow().return_type(), _ => panic!(), } } @@ -155,9 +192,11 @@ impl Call { _ => panic!("Calling things other than identifiers not yet supported."), }; + let function_symbol = function_symbol.borrow(); if function_symbol.is_platform() { + let arg_count = function_symbol.parameters().len(); context.instruction(AsmInstruction::InvokePlatformStatic( - InvokePlatformStatic::new(function_symbol.name()), + InvokePlatformStatic::new(function_symbol.name(), arg_count), )); } else { todo!("non-platform invoke") diff --git a/dmc-lib/src/ast/extern_function.rs b/dmc-lib/src/ast/extern_function.rs index 04db97c..f038863 100644 --- a/dmc-lib/src/ast/extern_function.rs +++ b/dmc-lib/src/ast/extern_function.rs @@ -1,20 +1,29 @@ use crate::ast::assemble_context::AssembleContext; +use crate::ast::parameter::Parameter; 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 std::cell::RefCell; +use std::rc::Rc; pub struct ExternFunction { declared_name: String, declared_name_source_range: SourceRange, + parameters: Vec, } impl ExternFunction { - pub fn new(name: &str, declared_name_source_range: SourceRange) -> ExternFunction { + pub fn new( + name: &str, + declared_name_source_range: SourceRange, + parameters: Vec, + ) -> ExternFunction { ExternFunction { declared_name: name.into(), declared_name_source_range, + parameters, } } @@ -23,26 +32,52 @@ impl ExternFunction { } pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec { - let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new( - &self.declared_name, - &vec![], - true, - )); + let mut diagnostics = vec![]; + + let insert_result = + symbol_table.insert_function_symbol(FunctionSymbol::new(&self.declared_name, true)); + + let mut maybe_function_symbol: Option>> = None; match insert_result { - Ok(_) => vec![], + Ok(function_symbol) => { + maybe_function_symbol = Some(function_symbol); + } Err(symbol_insert_error) => match symbol_insert_error { SymbolInsertError::AlreadyDeclared(already_declared) => { - vec![Diagnostic::new( + diagnostics.push(Diagnostic::new( &format!( "Function {} already declared in current scope.", already_declared.name() ), self.declared_name_source_range.start(), self.declared_name_source_range.end(), - )] + )); } }, } + + symbol_table.push_scope(&format!("extern_function_scope({})", &self.declared_name)); + + let mut parameter_symbols = vec![]; + for parameter in &mut self.parameters { + let parameter_result = parameter.gather_declared_names(symbol_table); + match parameter_result { + Ok(parameter_symbol) => { + parameter_symbols.push(parameter_symbol); + } + Err(mut parameter_diagnostics) => { + diagnostics.append(&mut parameter_diagnostics); + } + } + } + maybe_function_symbol + .unwrap() + .borrow_mut() + .set_parameters(parameter_symbols); + + symbol_table.pop_scope(); + + diagnostics } pub fn check_name_usages(&mut self, symbol_table: &SymbolTable) -> Vec { diff --git a/dmc-lib/src/ast/function.rs b/dmc-lib/src/ast/function.rs index 61f1a7a..8da140b 100644 --- a/dmc-lib/src/ast/function.rs +++ b/dmc-lib/src/ast/function.rs @@ -40,11 +40,8 @@ impl Function { pub fn gather_declared_names(&mut self, symbol_table: &mut SymbolTable) -> Vec { let mut diagnostics = vec![]; // insert function symbol - let insert_result = symbol_table.insert_function_symbol(FunctionSymbol::new( - self.declared_name(), - &vec![], // todo - false, - )); + let insert_result = + symbol_table.insert_function_symbol(FunctionSymbol::new(self.declared_name(), false)); if let Err(symbol_insert_error) = insert_result { match symbol_insert_error { SymbolInsertError::AlreadyDeclared(already_declared) => { @@ -59,6 +56,9 @@ impl Function { } } } + + // todo: parameters + symbol_table.push_scope(&format!("function_scope({})", self.declared_name())); for statement in &mut self.statements { diagnostics.append(&mut statement.gather_declared_names(symbol_table)); diff --git a/dmc-lib/src/ast/mod.rs b/dmc-lib/src/ast/mod.rs index 4bde6d3..f422696 100644 --- a/dmc-lib/src/ast/mod.rs +++ b/dmc-lib/src/ast/mod.rs @@ -10,5 +10,6 @@ pub mod identifier; pub mod integer_literal; pub mod let_statement; pub mod module_level_declaration; +pub mod parameter; pub mod statement; pub mod string_literal; diff --git a/dmc-lib/src/ast/parameter.rs b/dmc-lib/src/ast/parameter.rs new file mode 100644 index 0000000..e2e45ae --- /dev/null +++ b/dmc-lib/src/ast/parameter.rs @@ -0,0 +1,41 @@ +use crate::diagnostic::Diagnostic; +use crate::source_range::SourceRange; +use crate::symbol::ParameterSymbol; +use crate::symbol_table::{SymbolInsertError, SymbolTable}; +use crate::type_info::TypeInfo; +use std::cell::RefCell; +use std::rc::Rc; + +pub struct Parameter { + declared_name: String, + declared_name_source_range: SourceRange, +} + +impl Parameter { + pub fn new(declared_name: &str, declared_name_source_range: SourceRange) -> Self { + Self { + declared_name: declared_name.into(), + declared_name_source_range, + } + } + + pub fn gather_declared_names( + &mut self, + symbol_table: &mut SymbolTable, + ) -> Result>, Vec> { + let insert_result = symbol_table.insert_parameter_symbol(ParameterSymbol::new( + &self.declared_name, + TypeInfo::String, // todo + )); + match insert_result { + Ok(parameter_symbol) => Ok(parameter_symbol), + Err(symbol_insert_error) => match symbol_insert_error { + SymbolInsertError::AlreadyDeclared(already_declared) => Err(vec![Diagnostic::new( + &format!("Parameter {} already declared.", already_declared.name()), + self.declared_name_source_range.start(), + self.declared_name_source_range.end(), + )]), + }, + } + } +} diff --git a/dmc-lib/src/ir/ir_call.rs b/dmc-lib/src/ir/ir_call.rs index 67d449e..27bf7ef 100644 --- a/dmc-lib/src/ir/ir_call.rs +++ b/dmc-lib/src/ir/ir_call.rs @@ -46,7 +46,7 @@ impl IrCall { } } instructions.push(AsmInstruction::InvokePlatformStatic( - InvokePlatformStatic::new(&self.name), + InvokePlatformStatic::new(&self.name, todo!()), )); instructions } diff --git a/dmc-lib/src/lexer.rs b/dmc-lib/src/lexer.rs index c7d4606..4a74f94 100644 --- a/dmc-lib/src/lexer.rs +++ b/dmc-lib/src/lexer.rs @@ -44,6 +44,8 @@ impl<'a> Lexer<'a> { ) } else if chunk.starts_with("=") { Token::new(self.position, self.position + 1, TokenKind::Equals) + } else if chunk.starts_with(",") { + Token::new(self.position, self.position + 1, TokenKind::Comma) } else { // more than one char token if chunk.starts_with(|c: char| c.is_ascii_digit()) { diff --git a/dmc-lib/src/parser.rs b/dmc-lib/src/parser.rs index 5099c85..4800860 100644 --- a/dmc-lib/src/parser.rs +++ b/dmc-lib/src/parser.rs @@ -8,6 +8,7 @@ use crate::ast::identifier::Identifier; use crate::ast::integer_literal::IntegerLiteral; use crate::ast::let_statement::LetStatement; use crate::ast::module_level_declaration::ModuleLevelDeclaration; +use crate::ast::parameter::Parameter; use crate::ast::statement::Statement; use crate::ast::string_literal::StringLiteral; use crate::diagnostic::Diagnostic; @@ -138,6 +139,13 @@ impl<'a> Parser<'a> { } } + fn peek_lookahead(&self, token_kind: TokenKind) -> bool { + match &self.lookahead { + None => panic!("Unexpected end of input."), + Some(token) => token.kind() == token_kind, + } + } + fn sample_input(&self, start: usize, end: usize) -> &'a str { &self.input[start..end] } @@ -251,10 +259,62 @@ impl<'a> Parser<'a> { self.expect_advance(TokenKind::Fn)?; let identifier_token = self.expect_advance(TokenKind::Identifier)?; self.expect_advance(TokenKind::LeftParentheses)?; - // params - self.expect_advance(TokenKind::RightParentheses)?; - // return type - Ok(ExternFunction::new( + + let mut diagnostics = vec![]; + + let mut maybe_parameters: Option> = None; + let params_result = self.parameter_list(); + match params_result { + Ok(parameters) => { + maybe_parameters = Some(parameters); + } + Err(mut parameter_list_diagnostics) => { + diagnostics.append(&mut parameter_list_diagnostics); + } + } + + let right_parentheses_result = self.expect_advance(TokenKind::RightParentheses); + match right_parentheses_result { + Err(mut right_parentheses_diagnostics) => { + diagnostics.append(&mut right_parentheses_diagnostics); + } + Ok(_) => {} + } + + if diagnostics.is_empty() { + Ok(ExternFunction::new( + self.token_text(&identifier_token), + SourceRange::new(identifier_token.start(), identifier_token.end()), + maybe_parameters.unwrap(), + )) + } else { + Err(diagnostics) + } + } + + fn parameter_list(&mut self) -> Result, Vec> { + let mut parameters = vec![]; + let mut diagnostics = vec![]; + while self.current.is_some() && self.peek_current(TokenKind::Identifier) { + let parameter_result = self.parameter(); + match parameter_result { + Ok(parameter) => { + parameters.push(parameter); + } + Err(mut parameter_diagnostics) => { + diagnostics.append(&mut parameter_diagnostics); + } + } + if self.lookahead.is_some() && self.peek_lookahead(TokenKind::Comma) { + self.advance(); + } + } + Ok(parameters) + } + + fn parameter(&mut self) -> Result> { + let identifier_token = self.expect_advance(TokenKind::Identifier)?; + Ok(Parameter::new( self.token_text(&identifier_token), SourceRange::new(identifier_token.start(), identifier_token.end()), )) @@ -381,9 +441,56 @@ impl<'a> Parser<'a> { mod smoke_tests { use super::*; + fn smoke_test(input: &str) { + let parse_result = parse_compilation_unit(input); + match parse_result { + Ok(_) => {} + Err(diagnostics) => { + eprintln!("{:#?}", diagnostics); + panic!("There were diagnostics during parsing"); + } + } + } + #[test] fn forty_two() { - parse_compilation_unit("fn main() 42 end"); + smoke_test("fn main() 42 end"); + } + + #[test] + fn chained_calls() { + smoke_test("fn main() getCl()() end"); + } + + #[test] + fn extern_fn_with_param() { + smoke_test("extern fn println(message)") + } +} + +#[cfg(test)] +mod concrete_tests { + use super::*; + + #[test] + fn parses_extern_fn() { + let parse_result = parse_compilation_unit("extern fn println()"); + let compilation_unit = match parse_result { + Ok(compilation_unit) => compilation_unit, + Err(diagnostics) => { + for diagnostic in diagnostics { + eprintln!("{:?}", diagnostic); + } + panic!(); + } + }; + let declarations = compilation_unit.declarations(); + assert_eq!(declarations.len(), 1); + let extern_function = match &declarations[0] { + ModuleLevelDeclaration::ExternFunction(extern_function) => extern_function, + _ => panic!(), + }; + assert_eq!(extern_function.declared_name(), "println"); } #[test] @@ -432,37 +539,6 @@ mod smoke_tests { panic!("Expected expression"); } } - - #[test] - fn chained_calls() { - parse_compilation_unit("fn main() getCl()() end"); - } -} - -#[cfg(test)] -mod concrete_tests { - use super::*; - - #[test] - fn parses_extern_fn() { - let parse_result = parse_compilation_unit("extern fn println()"); - let compilation_unit = match parse_result { - Ok(compilation_unit) => compilation_unit, - Err(diagnostics) => { - for diagnostic in diagnostics { - eprintln!("{:?}", diagnostic); - } - panic!(); - } - }; - let declarations = compilation_unit.declarations(); - assert_eq!(declarations.len(), 1); - let extern_function = match &declarations[0] { - ModuleLevelDeclaration::ExternFunction(extern_function) => extern_function, - _ => panic!(), - }; - assert_eq!(extern_function.declared_name(), "println"); - } } #[cfg(test)] diff --git a/dmc-lib/src/scope.rs b/dmc-lib/src/scope.rs index 8e2d68b..5804a53 100644 --- a/dmc-lib/src/scope.rs +++ b/dmc-lib/src/scope.rs @@ -6,7 +6,7 @@ use std::rc::Rc; pub struct Scope { debug_name: String, parent_id: Option, - function_symbols: HashMap, Rc>, + function_symbols: HashMap, Rc>>, parameter_symbols: HashMap, Rc>>, variable_symbols: HashMap, Rc>>, } @@ -22,11 +22,11 @@ impl Scope { } } - pub fn function_symbols(&self) -> &HashMap, Rc> { + pub fn function_symbols(&self) -> &HashMap, Rc>> { &self.function_symbols } - pub fn function_symbols_mut(&mut self) -> &mut HashMap, Rc> { + pub fn function_symbols_mut(&mut self) -> &mut HashMap, Rc>> { &mut self.function_symbols } diff --git a/dmc-lib/src/symbol.rs b/dmc-lib/src/symbol.rs index b3d2fba..f5c53eb 100644 --- a/dmc-lib/src/symbol.rs +++ b/dmc-lib/src/symbol.rs @@ -4,16 +4,16 @@ use std::rc::Rc; pub struct FunctionSymbol { name: Rc, - parameters: Vec>, is_platform: bool, + parameters: Option>>>, } impl FunctionSymbol { - pub fn new(name: &str, parameters: &[Rc], is_platform: bool) -> Self { + pub fn new(name: &str, is_platform: bool) -> Self { Self { name: name.into(), - parameters: parameters.into(), is_platform, + parameters: None, } } @@ -25,8 +25,12 @@ impl FunctionSymbol { self.name.clone() } - pub fn parameters(&self) -> &[Rc] { - &self.parameters + pub fn set_parameters(&mut self, parameters: Vec>>) { + self.parameters = Some(parameters); + } + + pub fn parameters(&self) -> &[Rc>] { + self.parameters.as_ref().unwrap() } pub fn return_type(&self) -> TypeInfo { @@ -111,7 +115,7 @@ impl VariableSymbol { } pub enum ExpressibleSymbol { - Function(Rc), + Function(Rc>), Parameter(Rc>), Variable(Rc>), } diff --git a/dmc-lib/src/symbol_table.rs b/dmc-lib/src/symbol_table.rs index 62bdfc1..4f0bd52 100644 --- a/dmc-lib/src/symbol_table.rs +++ b/dmc-lib/src/symbol_table.rs @@ -51,32 +51,35 @@ impl SymbolTable { pub fn insert_function_symbol( &mut self, function_symbol: FunctionSymbol, - ) -> Result<(), SymbolInsertError> { + ) -> Result>, SymbolInsertError> { if self.current_scope_has_name(function_symbol.name()) { return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new( function_symbol.name(), ))); } + let name = function_symbol.name_owned(); + let as_rc = Rc::new(RefCell::new(function_symbol)); + self.current_scope_mut() .function_symbols_mut() - .insert(function_symbol.name_owned(), Rc::new(function_symbol)); - Ok(()) + .insert(name, as_rc.clone()); + Ok(as_rc) } pub fn insert_parameter_symbol( &mut self, parameter_symbol: ParameterSymbol, - ) -> Result<(), SymbolInsertError> { + ) -> Result>, SymbolInsertError> { if self.current_scope_has_name(parameter_symbol.name()) { return Err(SymbolInsertError::AlreadyDeclared(AlreadyDeclared::new( parameter_symbol.name(), ))); } - self.current_scope_mut().parameter_symbols_mut().insert( - parameter_symbol.name_owned(), - Rc::new(RefCell::new(parameter_symbol)), - ); - Ok(()) + let as_rc = Rc::new(RefCell::new(parameter_symbol)); + self.current_scope_mut() + .parameter_symbols_mut() + .insert(as_rc.borrow().name_owned(), as_rc.clone()); + Ok(as_rc) } pub fn insert_variable_symbol( diff --git a/dmc-lib/src/token.rs b/dmc-lib/src/token.rs index fe41073..e41ab24 100644 --- a/dmc-lib/src/token.rs +++ b/dmc-lib/src/token.rs @@ -36,4 +36,5 @@ pub enum TokenKind { LongLiteral, String, Extern, + Comma, } diff --git a/dmc-lib/src/type_info.rs b/dmc-lib/src/type_info.rs index 3d161e0..f3a7c0e 100644 --- a/dmc-lib/src/type_info.rs +++ b/dmc-lib/src/type_info.rs @@ -1,9 +1,43 @@ use crate::symbol::FunctionSymbol; +use std::cell::RefCell; +use std::fmt::{Display, Formatter}; use std::rc::Rc; #[derive(Clone)] pub enum TypeInfo { Integer, String, - Function(Rc), + Function(Rc>), +} + +impl Display for TypeInfo { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let s = match self { + TypeInfo::Integer => "Int", + TypeInfo::String => "String", + TypeInfo::Function(_) => "fn", // todo + }; + write!(f, "{}", s) + } +} + +impl PartialEq for &TypeInfo { + fn eq(&self, other: &Self) -> bool { + match self { + TypeInfo::Integer => match other { + TypeInfo::Integer => true, + _ => false, + }, + TypeInfo::String => match other { + TypeInfo::String => true, + _ => false, + }, + TypeInfo::Function(f0) => match other { + TypeInfo::Function(f1) => { + f0.as_ptr() == f1.as_ptr() // todo + } + _ => false, + }, + } + } } diff --git a/dvm-lib/src/instruction.rs b/dvm-lib/src/instruction.rs index 1e0b26e..e9deecf 100644 --- a/dvm-lib/src/instruction.rs +++ b/dvm-lib/src/instruction.rs @@ -3,6 +3,7 @@ use std::rc::Rc; pub type Register = usize; pub type ConstantName = Rc; pub type FunctionName = Rc; +pub type ArgCount = usize; pub enum Instruction { MoveRegister(Register, Register), @@ -13,7 +14,7 @@ pub enum Instruction { PushInt(i32), PushStackFrameOffset(isize), - InvokePlatformStatic(FunctionName), + InvokePlatformStatic(FunctionName, ArgCount), LoadStringConstant(ConstantName, Register), diff --git a/dvm-lib/src/lib.rs b/dvm-lib/src/lib.rs index b7c1352..88c25a3 100644 --- a/dvm-lib/src/lib.rs +++ b/dvm-lib/src/lib.rs @@ -1,2 +1,3 @@ pub mod instruction; +pub mod platform_function; pub mod vm; diff --git a/dvm-lib/src/platform_function.rs b/dvm-lib/src/platform_function.rs new file mode 100644 index 0000000..4666fac --- /dev/null +++ b/dvm-lib/src/platform_function.rs @@ -0,0 +1,6 @@ +use crate::vm::value::Value; +use crate::vm::{DvmContext, DvmState}; +use std::error::Error; + +pub type PlatformFunction = + fn(context: &DvmContext, state: &DvmState, args: &[Value]) -> Result>; diff --git a/dvm-lib/src/vm/mod.rs b/dvm-lib/src/vm/mod.rs index d2d1cb4..e37cfb0 100644 --- a/dvm-lib/src/vm/mod.rs +++ b/dvm-lib/src/vm/mod.rs @@ -1,4 +1,5 @@ use crate::instruction::Instruction; +use crate::platform_function::PlatformFunction; use crate::vm::constant::Constant; use crate::vm::function::Function; use crate::vm::value::Value; @@ -11,6 +12,7 @@ pub mod value; pub struct DvmContext { functions: HashMap, Function>, + platform_functions: HashMap, PlatformFunction>, constants: HashMap, Constant>, } @@ -18,10 +20,27 @@ impl DvmContext { pub fn new() -> Self { Self { functions: HashMap::new(), + platform_functions: HashMap::new(), constants: HashMap::new(), } } + pub fn functions(&self) -> &HashMap, Function> { + &self.functions + } + + pub fn functions_mut(&mut self) -> &mut HashMap, Function> { + &mut self.functions + } + + pub fn platform_functions(&self) -> &HashMap, PlatformFunction> { + &self.platform_functions + } + + pub fn platform_functions_mut(&mut self) -> &mut HashMap, PlatformFunction> { + &mut self.platform_functions + } + pub fn add_function(&mut self, function: Function) { self.functions.insert(function.name_owned(), function); } @@ -155,10 +174,28 @@ pub fn call( } /* Invoke instructions */ - Instruction::InvokePlatformStatic(function_name) => { - if function_name.as_ref() == "println" { - println!("{:?}", state.stack()); - println!("{:?}", state.registers()); + Instruction::InvokePlatformStatic(function_name, arg_count) => { + let stack = state.stack(); + let args = &stack[stack.len() - arg_count..]; + let platform_function = context + .platform_functions() + .get(function_name) + .expect(&format!("Platform function {} not found", function_name)); + let result = platform_function(context, state, args); + match result { + Ok(return_value) => { + // pop args off the stack + let mut i: usize = 0; + while i < *arg_count { + state.stack_mut().pop(); + i += 1; + } + state.stack_mut().push(return_value); + } + Err(error) => { + // Eventually we will have some kind of exception handling + panic!("{}", error); + } } } @@ -183,5 +220,5 @@ pub fn call( } state.increment_ip(); } - None + None // todo: returning results from main functions } diff --git a/examples/hello.dm b/examples/hello.dm index 84cb693..ed2a17f 100644 --- a/examples/hello.dm +++ b/examples/hello.dm @@ -1,4 +1,4 @@ -extern fn println() +extern fn println(message) fn main() let x = "Hello, World!"