diff --git a/src/ast_compiler.rs b/src/ast_compiler.rs index 630d930..f19ca9a 100644 --- a/src/ast_compiler.rs +++ b/src/ast_compiler.rs @@ -1,17 +1,20 @@ -use crate::ast_compiler::Expression::Variable; -use crate::tokens::TokenType::{ - Bang, Bool, Char, Colon, Date, Eol, Equal, F32, F64, False, FloatingPoint, Greater, - GreaterEqual, GreaterGreater, I32, I64, Identifier, Integer, LeftParen, Less, LessEqual, - LessLess, Let, ListType, MapType, Minus, Object, Plus, Print, RightParen, Slash, Star, Text, - True, U32, U64, -}; +use crate::tokens::TokenType::{Bang, Bool, Char, Colon, Date, Eol, Equal, F32, F64, False, FloatingPoint, Fn, Greater, GreaterEqual, GreaterGreater, I32, I64, Identifier, Indent, Integer, LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus, Print, RightParen, SingleRightArrow, Slash, Star, True, U32, U64, StringType}; use crate::tokens::{Token, TokenType}; use crate::value::Value; use log::debug; +use std::collections::HashMap; pub fn compile(tokens: Vec) -> anyhow::Result> { let mut compiler = AstCompiler::new(tokens); - compiler.compile() + compiler.compile(0) +} + +#[derive(Debug, Clone)] +pub(crate) struct Function { + pub(crate) name: Token, + pub(crate) parameters: Vec, + pub(crate) return_type: TokenType, + pub(crate) body: Vec, } struct AstCompiler { @@ -19,6 +22,8 @@ struct AstCompiler { current: usize, had_error: bool, vars: Vec, + indent: Vec, + functions: HashMap, } impl AstCompiler { @@ -28,25 +33,109 @@ impl AstCompiler { current: 0, had_error: false, vars: vec![], + indent: vec![], + functions: HashMap::new(), } } - fn compile(&mut self) -> anyhow::Result> { + fn compile(&mut self, expected_indent: usize) -> anyhow::Result> { let mut statements = vec![]; while !self.is_at_end() { - statements.push(self.declaration()?) + let statement = self.indent(expected_indent)?; + if let Some(statement) = statement { + statements.push(statement); + } else { + break; + } } Ok(statements) } + fn indent(&mut self, expected_indent: usize) -> anyhow::Result> { + // skip empty lines + while self.check(Eol) { + self.advance(); + } + + let mut indent_on_line = 0; + // keep track of indent level + while self.match_token(vec![Indent]) { + indent_on_line += 1; + } + if indent_on_line > expected_indent { + panic!( + "unexpected indent level {} vs {}", + indent_on_line, expected_indent + ); + } else if indent_on_line < expected_indent { + self.indent.pop(); + return Ok(None); + } else { + self.indent.push(indent_on_line); + Ok(Some(self.declaration()?)) + } + } + fn declaration(&mut self) -> anyhow::Result { - if self.match_token(vec![Let]) { + if self.match_token(vec![Fn]) { + self.function_declaration() + } else if self.match_token(vec![Let]) { self.let_declaration() } else { self.statement() } } + fn function_declaration(&mut self) -> anyhow::Result { + let name_token = self.consume(Identifier, "Expect function name.")?; + self.consume(LeftParen, "Expect '(' after function name.")?; + let mut parameters = vec![]; + while !self.check(RightParen) { + if parameters.len() >= 25 { + return Err(anyhow::anyhow!("Too many parameters.")); + } + let parm_name = self.consume(Identifier, "Expect parameter name.")?; + + self.consume(Colon, "Expect : after parameter name")?; + let var_type = self.peek().token_type; + self.vars.push(Expression::Variable { + name: parm_name.lexeme.to_string(), + var_type, + line: parm_name.line, + }); + self.advance(); + parameters.push(Parameter { + name: parm_name, + var_type, + }); + } + self.consume(RightParen, "Expect ')' after parameters.")?; + let return_type = if self.check(SingleRightArrow) { + self.consume(SingleRightArrow, "")?; + self.advance().token_type + } else { + TokenType::Void + }; + self.consume(Colon, "Expect colon (:) after function declaration.")?; + self.consume(Eol, "Expect end of line.")?; + + let current_indent = self.indent.last().unwrap(); + let body = self.compile(current_indent + 1)?; + + let function = Function { + name: name_token.clone(), + parameters, + return_type, + body, + }; + + let function_stmt = Statement::FunctionStmt { + function: function.clone(), + }; + self.functions.insert(name_token.lexeme, function.clone()); + Ok(function_stmt) + } + fn let_declaration(&mut self) -> anyhow::Result { let name_token = self.consume(Identifier, "Expect variable name.")?; @@ -70,7 +159,7 @@ impl AstCompiler { return Err(e); } }; - self.vars.push(Variable { + self.vars.push(Expression::Variable { name: name_token.lexeme.to_string(), var_type, line: name_token.line, @@ -95,8 +184,8 @@ impl AstCompiler { fn print_statement(&mut self) -> anyhow::Result { let expr = self.expression()?; - self.consume(Eol, "Expect end of line after expression.")?; - Ok(Statement::Print { value: expr }) + self.consume(Eol, "Expect end of line after print statement.")?; + Ok(Statement::PrintStmt { value: expr }) } fn expr_statement(&mut self) -> anyhow::Result { @@ -192,6 +281,7 @@ impl AstCompiler { } fn primary(&mut self) -> anyhow::Result { + debug!("primary {:?}", self.peek()); Ok(if self.match_token(vec![False]) { Expression::Literal { line: self.peek().line, @@ -216,10 +306,10 @@ impl AstCompiler { literaltype: FloatingPoint, value: Value::F64(self.previous().lexeme.parse()?), } - } else if self.match_token(vec![Text]) { + } else if self.match_token(vec![StringType]) { Expression::Literal { line: self.peek().line, - literaltype: Text, + literaltype: StringType, value: Value::String(self.previous().lexeme.to_string()), } } else if self.match_token(vec![LeftParen]) { @@ -231,26 +321,70 @@ impl AstCompiler { } } else { let token = self.advance().clone(); - let (var_name, var_type) = self - .vars - .iter() - .filter_map(|e| { - if let Variable { name, var_type, .. } = e { - Some((name, var_type)) - } else { - None - } - }) - .find(|e| e.0 == &token.lexeme) - .ok_or_else(|| return anyhow::anyhow!("Unknown variable: {}", token.lexeme))?; - Variable { - name: var_name.to_string(), - var_type: var_type.clone(), - line: token.line, + debug!("{:?}", token); + if self.match_token(vec![LeftParen]) { + self.function_call(token.lexeme)? + } else { + self.variable_lookup(&token)? } }) } + fn variable_lookup(&mut self, token: &Token) -> anyhow::Result { + let (var_name, var_type) = self + .vars + .iter() + .filter_map(|e| { + if let Expression::Variable { name, var_type, .. } = e { + Some((name, var_type)) + } else { + None + } + }) + .find(|e| e.0 == &token.lexeme) + .ok_or_else(|| return anyhow::anyhow!("Unknown variable: {:?}", token))?; + Ok(Expression::Variable { + name: var_name.to_string(), + var_type: var_type.clone(), + line: token.line, + }) + } + + fn function_call(&mut self, name: String) -> anyhow::Result { + let function_name = self.functions.get(&name).unwrap().name.lexeme.clone(); + let function = self.functions.get(&function_name).unwrap().clone(); + + let mut arguments = vec![]; + while !self.match_token(vec![RightParen]) { + if arguments.len() >= 25 { + return Err(anyhow::anyhow!("Too many parameters.")); + } + let arg = self.expression()?; + let arg_type = arg.infer_type(); + if arg_type != function.parameters[arguments.len()].var_type { + return Err(anyhow::anyhow!( + "Incompatible argument types. Expected {}, found {}", + function.parameters[arguments.len()].var_type, + arg_type + )); + } + arguments.push(arg); + if self.peek().token_type == TokenType::Comma { + self.advance(); + } else { + self.consume(RightParen, "Expect ')' after arguments.")?; + break; + } + } + let return_type = self.functions.get(&name).unwrap().return_type; + Ok(Expression::FunctionCall { + line: self.peek().line, + name, + arguments, + return_type, + }) + } + fn consume(&mut self, token_type: TokenType, message: &str) -> anyhow::Result { if self.check(token_type) { self.advance(); @@ -303,7 +437,9 @@ fn calculate_type( declared_type: Option, inferred_type: TokenType, ) -> anyhow::Result { + println!("declared type {:?} inferred type: {:?}", declared_type, inferred_type); Ok(if let Some(declared_type) = declared_type { + if declared_type != inferred_type { match (declared_type, inferred_type) { (I32, I64) => I32, @@ -312,6 +448,7 @@ fn calculate_type( (F64, I64) => F64, (U64, I64) => U64, (U64, I32) => U64, + (StringType, Text) => StringType, _ => { return Err(anyhow::anyhow!( "Incompatible types. Expected {}, found {}", @@ -338,7 +475,7 @@ fn calculate_type( }) } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Statement { ExpressionStmt { expression: Expression, @@ -348,26 +485,32 @@ pub enum Statement { var_type: TokenType, initializer: Expression, }, - Print { + PrintStmt { value: Expression, }, + FunctionStmt { + function: Function, + }, } impl Statement { pub fn line(&self) -> usize { match self { Statement::ExpressionStmt { expression } => expression.line(), - Statement::VarStmt { - name, - var_type, - initializer, - } => name.line, - Statement::Print { value } => value.line(), + Statement::VarStmt { name, .. } => name.line, + Statement::PrintStmt { value } => value.line(), + Statement::FunctionStmt { function, .. } => function.name.line, } } } -#[derive(Debug)] +#[derive(Debug, Clone)] +pub struct Parameter { + pub(crate) name: Token, + pub(crate) var_type: TokenType, +} + +#[derive(Debug, Clone)] pub enum Expression { Binary { line: usize, @@ -394,6 +537,12 @@ pub enum Expression { name: String, var_type: TokenType, }, + FunctionCall { + line: usize, + name: String, + arguments: Vec, + return_type: TokenType, + }, } impl Expression { @@ -404,6 +553,7 @@ impl Expression { Self::Grouping { line, expression } => *line, Self::Literal { line, .. } => *line, Self::Variable { line, .. } => *line, + Self::FunctionCall { line, .. } => *line, } } @@ -432,8 +582,8 @@ impl Expression { // followed by type coercion to 64 bits for numeric types debug!("coerce {} : {}", left_type, right_type); match (left_type, right_type) { - (_, Text) => Text, - (Text, _) => Text, + (_, StringType) => StringType, + (StringType, _) => StringType, (FloatingPoint, _) => F64, (Integer, FloatingPoint) => F64, (Integer, _) => I64, @@ -469,6 +619,7 @@ impl Expression { Self::Literal { literaltype, .. } => literaltype.clone(), Self::Unary { right, .. } => right.infer_type(), Self::Variable { var_type, .. } => var_type.clone(), + Self::FunctionCall { return_type, .. } => return_type.clone(), } } } diff --git a/src/bytecode_compiler.rs b/src/bytecode_compiler.rs index 5e1d908..99fd10b 100644 --- a/src/bytecode_compiler.rs +++ b/src/bytecode_compiler.rs @@ -1,12 +1,34 @@ -use std::collections::HashMap; -use crate::ast_compiler::{Expression, Statement}; +use crate::ast_compiler::{Expression, Function, Parameter, Statement}; use crate::chunk::Chunk; use crate::tokens::TokenType; use crate::value::Value; -use crate::vm::{OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_CHAR, OP_DEF_DATE, OP_DEF_F32, OP_DEF_F64, OP_DEF_I32, OP_DEF_I64, OP_DEF_LIST, OP_DEF_MAP, OP_DEF_STRING, OP_DEF_STRUCT, OP_DEFINE, OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT, OP_AND, OP_OR}; +use crate::vm::{ + OP_ADD, OP_AND, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_CHAR, + OP_DEF_DATE, OP_DEF_F32, OP_DEF_F64, OP_DEF_FN, OP_DEF_I32, OP_DEF_I64, OP_DEF_LIST, + OP_DEF_MAP, OP_DEF_STRING, OP_DEF_STRUCT, OP_DEFINE, OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER, + OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_OR, OP_PRINT, + OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT, +}; +use std::collections::HashMap; -pub fn compile(ast: Vec) -> anyhow::Result { - let compiler = Compiler::new(); +pub fn compile(ast: &Vec) -> anyhow::Result { + compile_name(ast, "_") +} + +pub(crate) fn compile_function(function: &Function) -> anyhow::Result { + let mut compiler = Compiler::new(&function.name.lexeme); + for parm in &function.parameters{ + let name = parm.name.lexeme.clone(); + let name_index = compiler.chunk.add_constant(Value::String(name.clone())); + compiler.vars.insert(name, name_index); + compiler.emit_bytes(OP_DEFINE, name_index as u16); + } + + Ok(compiler.compile(&function.body)?) +} + +pub(crate) fn compile_name(ast: &Vec, name: &str) -> anyhow::Result { + let compiler = Compiler::new(name); Ok(compiler.compile(ast)?) } @@ -15,20 +37,22 @@ struct Compiler { had_error: bool, current_line: usize, vars: HashMap, + functions: HashMap, } impl Compiler { - fn new() -> Self { + fn new(name: &str) -> Self { Self { - chunk: Chunk::new("main"), + chunk: Chunk::new(name), had_error: false, current_line: 0, vars: HashMap::new(), + functions: HashMap::new(), } } - fn compile(mut self, ast: Vec) -> anyhow::Result { - for statement in &ast { + fn compile(mut self, ast: &Vec) -> anyhow::Result { + for statement in ast { self.compile_statement(statement)? } @@ -45,22 +69,41 @@ impl Compiler { initializer, } => { let name_index = self.chunk.add_constant(Value::String(name.lexeme.clone())); - self.vars.insert(name.lexeme.clone(),name_index); + self.vars.insert(name.lexeme.clone(), name_index); self.compile_expression(initializer)?; self.define_variable(var_type, name_index)? } - Statement::Print { value } => { + Statement::PrintStmt { value } => { self.compile_expression(value)?; self.emit_byte(OP_PRINT); } - _ => unimplemented!(), + Statement::ExpressionStmt { expression } => { + self.compile_expression(expression)?; + } + Statement::FunctionStmt { function } => { + let function_name = function.name.lexeme.clone(); + let compiled_function = compile_function(function)?; + let name_index = self.chunk.add_function(compiled_function); + self.functions + .insert(function_name, name_index); + } } Ok(()) } fn compile_expression(&mut self, expression: &Expression) -> anyhow::Result<()> { match expression { - Expression::Variable {name, ..} => { + Expression::FunctionCall { + name, arguments, .. + } => { + let function_index = *self.functions.get(name).unwrap(); + for argument in arguments { + self.compile_expression(argument)?; + } + self.emit_bytes(OP_CALL, function_index as u16); + self.emit_byte(arguments.len() as u16); + } + Expression::Variable { name, .. } => { let name_index = self.vars.get(name).unwrap(); self.emit_bytes(OP_GET, *name_index as u16); } @@ -121,7 +164,7 @@ impl Compiler { TokenType::F32 => OP_DEF_F32, TokenType::F64 => OP_DEF_F64, TokenType::Date => OP_DEF_DATE, - TokenType::String => OP_DEF_STRING, + TokenType::StringType => OP_DEF_STRING, TokenType::Char => OP_DEF_CHAR, TokenType::Bool => OP_DEF_BOOL, TokenType::ListType => OP_DEF_LIST, diff --git a/src/chunk.rs b/src/chunk.rs index 9100a3c..36942e6 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -1,10 +1,10 @@ -use tracing::debug; +use std::collections::HashMap; use crate::value::Value; use crate::vm::{ OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CONSTANT, OP_DIVIDE, OP_MULTIPLY, OP_NEGATE, OP_RETURN, OP_SUBTRACT, OP_NOT, OP_SHL, OP_SHR, OP_LESS, OP_LESS_EQUAL, OP_GREATER, OP_GREATER_EQUAL, OP_EQUAL, OP_PRINT, OP_POP, OP_DEFINE, OP_GET,OP_DEF_STRING, - OP_DEF_I32, OP_DEF_I64, OP_DEF_F32, OP_DEF_F64, OP_DEF_BOOL + OP_DEF_I32, OP_DEF_I64, OP_DEF_F32, OP_DEF_F64, OP_DEF_BOOL, OP_CALL }; pub struct Chunk { @@ -12,8 +12,10 @@ pub struct Chunk { pub code: Vec, pub constants: Vec, lines: Vec, + pub(crate) functions: Vec } + impl Chunk { pub fn new(name: &str) -> Chunk { Chunk { @@ -21,6 +23,7 @@ impl Chunk { code: Vec::new(), constants: vec![], lines: vec![], + functions: Vec::new(), } } @@ -34,7 +37,15 @@ impl Chunk { self.constants.len() - 1 } + pub fn add_function(&mut self, function: Chunk) -> usize { + self.functions.push(function); + self.functions.len() - 1 + } + pub fn disassemble(&self) { + for f in &self.functions{ + f.disassemble(); + } println!("== {} ==", self.name); let mut offset = 0; while offset < self.code.len() { @@ -79,6 +90,7 @@ impl Chunk { OP_DEF_F32 => self.constant_inst("DEFF32", offset), OP_DEF_F64 => self.constant_inst("DEFF64", offset), OP_DEF_BOOL => self.constant_inst("DEFBOOL", offset), + OP_CALL => self.call_inst("CALL", offset), OP_GET => self.constant_inst("GET", offset), _ => { println!("Unknown instruction {}", instruction); @@ -92,6 +104,13 @@ impl Chunk { offset + 1 } + fn call_inst(&self, name: &str, offset: usize) -> usize { + let constant = self.code[offset + 1]; + let num_args = self.code[offset + 2]; + println!("{} {}({}):", name, constant, num_args); + offset + 3 + } + fn constant_inst(&self, name: &str, offset: usize) -> usize { let constant = self.code[offset + 1]; print!("{} {}:", name, constant); diff --git a/src/compiler.rs b/src/compiler.rs index 0f963eb..217e142 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -84,7 +84,7 @@ impl<'a> Compiler<'a> { | TokenType::U32 | TokenType::U64 | TokenType::Date - | TokenType::String + | TokenType::StringType | TokenType::Char | TokenType::Bool | TokenType::ListType @@ -129,14 +129,14 @@ impl<'a> Compiler<'a> { Some(TokenType::U32) => OP_DEF_I64, Some(TokenType::U64) => OP_DEF_I64, Some(TokenType::Date) => OP_DEF_DATE, - Some(TokenType::String) => OP_DEF_STRING, + Some(TokenType::StringType) => OP_DEF_STRING, Some(TokenType::Char) => OP_DEF_CHAR, Some(TokenType::Bool) => OP_DEF_BOOL, Some(TokenType::ListType) => OP_DEF_LIST, Some(TokenType::MapType) => OP_DEF_MAP, Some(TokenType::Object) => OP_DEF_STRUCT, _ => match derived_type { - Some(TokenType::Text) => OP_DEF_STRING, + Some(TokenType::StringType) => OP_DEF_STRING, Some(TokenType::Bool) => OP_DEF_BOOL, Some(TokenType::Char) => OP_DEF_CHAR, Some(TokenType::F64) => OP_DEF_F64, @@ -336,8 +336,8 @@ fn literal(s: &mut Compiler, expected_type: Option) -> anyhow::Result s.typestack.push(TokenType::Bool); s.emit_constant(Value::Bool(true)) } - (TokenType::Text, TokenType::String) => { - s.typestack.push(TokenType::String); + (TokenType::StringType, TokenType::StringType) => { + s.typestack.push(TokenType::StringType); s.emit_constant(Value::String(s.previous_token.lexeme.clone())) } //list @@ -356,7 +356,7 @@ fn literal(s: &mut Compiler, expected_type: Option) -> anyhow::Result match actual_type { TokenType::False => s.emit_constant(Value::Bool(false)), TokenType::True => s.emit_constant(Value::Bool(true)), - TokenType::Text => s.emit_constant(Value::String(s.previous_token.lexeme.clone())), + TokenType::StringType => s.emit_constant(Value::String(s.previous_token.lexeme.clone())), _ => {} } } @@ -506,12 +506,12 @@ static RULES: LazyLock> = LazyLock::new(|| { rules.insert(TokenType::RightBracket, Rule::new(None, None, PREC_NONE)); rules.insert(TokenType::Slash, Rule::new(None, Some(binary), PREC_FACTOR)); rules.insert(TokenType::Star, Rule::new(None, Some(binary), PREC_FACTOR)); - rules.insert(TokenType::Text, Rule::new(Some(literal), None, PREC_NONE)); + rules.insert(TokenType::StringType, Rule::new(Some(literal), None, PREC_NONE)); rules.insert( TokenType::BitAnd, Rule::new(None, Some(binary), PREC_BITAND), ); - rules.insert(TokenType::String, Rule::new(None, None, PREC_NONE)); + rules.insert(TokenType::StringType, Rule::new(None, None, PREC_NONE)); rules.insert(TokenType::Struct, Rule::new(None, None, PREC_NONE)); rules.insert(TokenType::True, Rule::new(Some(literal), None, PREC_NONE)); rules.insert(TokenType::U32, Rule::new(None, None, PREC_NONE)); diff --git a/src/keywords.rs b/src/keywords.rs index bbfcb40..42ce9ee 100644 --- a/src/keywords.rs +++ b/src/keywords.rs @@ -22,7 +22,7 @@ pub(crate) fn get_keyword(lexeme: &str) -> Option { "object" => Some(TokenType::Object), "print" => Some(TokenType::Print), "struct" => Some(TokenType::Struct), - "string" => Some(TokenType::String), + "string" => Some(TokenType::StringType), "true" => Some(TokenType::True), "u32" => Some(TokenType::U32), "u64" => Some(TokenType::U64), diff --git a/src/main.rs b/src/main.rs index 94422f7..3535a04 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,20 +1,25 @@ -use crudlang::{ast_compiler, chunk}; +use crudlang::ast_compiler; use crudlang::bytecode_compiler::compile; use crudlang::scanner::scan; -use crudlang::vm::{interpret, Vm}; +use crudlang::vm::interpret; fn main() -> anyhow::Result<()> { tracing_subscriber::fmt::init(); - let tokens = scan(r#"let a = 1>2 -print a"#); - // println!("{:?}", tokens); + let tokens = scan( + r#" +fn main(a: string) -> u32: + a + 42 +let text = "hello " +main(text)"#, + ); + println!("{:?}", tokens); match ast_compiler::compile(tokens) { Ok(statements) => { println!("{:?}", statements); - let chunk = compile(statements)?; + let chunk = compile(&statements)?; chunk.disassemble(); - interpret(chunk); + println!("{}",interpret(&chunk)?); } Err(e) => { println!("{}", e) diff --git a/src/scanner.rs b/src/scanner.rs index f14de88..fcbbe05 100644 --- a/src/scanner.rs +++ b/src/scanner.rs @@ -48,7 +48,14 @@ impl Scanner { ']' => self.add_token(TokenType::RightBracket), ',' => self.add_token(TokenType::Comma), '.' => self.add_token(TokenType::Dot), - '-' => self.add_token(TokenType::Minus), + '-' => { + let t = if self.match_next('>') { + TokenType::SingleRightArrow + } else { + TokenType::Minus + }; + self.add_token(t); + } '+' => self.add_token(TokenType::Plus), ':' => self.add_token(TokenType::Colon), ';' => println!("Warning: Ignoring semicolon at line {}", self.line), @@ -180,7 +187,7 @@ impl Scanner { let value: String = self.chars[self.start + 1..self.current - 1] .iter() .collect(); - self.add_token_with_value(TokenType::Text, value); + self.add_token_with_value(TokenType::StringType, value); } fn peek(&self) -> char { diff --git a/src/tokens.rs b/src/tokens.rs index 861af61..a484d71 100644 --- a/src/tokens.rs +++ b/src/tokens.rs @@ -78,21 +78,22 @@ pub enum TokenType { RightBrace, RightBracket, Semicolon, + SingleRightArrow, Slash, Star, - Text, - String, + StringType, Struct, True, U32, U64, + Void, While, } impl fmt::Display for TokenType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - TokenType::String => write!(f, "string"), + TokenType::StringType => write!(f, "string"), TokenType::Date => write!(f, "date"), TokenType::Char => write!(f, "char"), TokenType::I32 => write!(f, "i32"), @@ -149,11 +150,12 @@ impl fmt::Display for TokenType { TokenType::RightBrace => write!(f, "}}"), TokenType::RightBracket => write!(f, "]"), TokenType::Semicolon => write!(f, ";"), + TokenType::SingleRightArrow => write!(f, "->"), TokenType::Slash => write!(f, "/"), TokenType::Star => write!(f, "*"), - TokenType::Text => write!(f, "text"), TokenType::Struct => write!(f, "struct"), TokenType::True => write!(f, "true"), + TokenType::Void => write!(f, "()"), TokenType::While => write!(f, "while"), } } diff --git a/src/vm.rs b/src/vm.rs index 7f29b05..75c057b 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -5,8 +5,8 @@ use std::collections::HashMap; use tracing::debug; macro_rules! define_var { - ($self:ident, $variant:ident) => {{ - let name = $self.read_constant(); + ($self:ident, $variant:ident, $chunk:ident) => {{ + let name = $self.read_name($chunk); let value = $self.pop(); if let Value::$variant(_) = value { $self.local_vars.insert(name, value); @@ -20,37 +20,47 @@ macro_rules! define_var { } pub struct Vm { - chunk: Chunk, ip: usize, stack: Vec, local_vars: HashMap, error_occurred: bool, } -pub fn interpret(chunk: Chunk) -> anyhow::Result { +pub fn interpret(chunk: &Chunk) -> anyhow::Result { let mut vm = Vm { - chunk, ip: 0, stack: vec![], local_vars: HashMap::new(), error_occurred: false, }; - vm.run() + vm.run(chunk, vec![]) +} + +pub fn interpret_function(chunk: &Chunk, args: Vec) -> anyhow::Result { + let mut vm = Vm { + ip: 0, + stack: vec![], + local_vars: HashMap::new(), + error_occurred: false, + }; + vm.run(chunk, args) } impl Vm { - fn run(&mut self) -> anyhow::Result { + fn run(&mut self, chunk: &Chunk, args: Vec) -> anyhow::Result { + for arg in args{ + self.push(arg); + } loop { if self.error_occurred { return Err(anyhow!("Error occurred")); } debug!("{:?}", self.stack); - let opcode = self.chunk.code[self.ip]; + let opcode = chunk.code[self.ip]; self.ip += 1; match opcode { OP_CONSTANT => { - let value = &self.chunk.constants[self.chunk.code[self.ip] as usize]; - self.ip += 1; + let value = &chunk.constants[self.read(chunk)]; self.push(value.clone()); } OP_ADD => binary_op(self, |a, b| a + b), @@ -97,38 +107,55 @@ impl Vm { println!("{}", v); } OP_DEFINE => { - let name = self.read_constant(); + let name = self.read_name(chunk); let value = self.pop(); self.local_vars.insert(name, value); } - OP_DEF_I32 => define_var!(self, I32), - OP_DEF_I64 => define_var!(self, I64), - OP_DEF_U32 => define_var!(self, U32), - OP_DEF_U64 => define_var!(self, U64), - OP_DEF_F32 => define_var!(self, F32), - OP_DEF_F64 => define_var!(self, F64), - OP_DEF_STRING => define_var!(self, String), - OP_DEF_CHAR => define_var!(self, Char), - OP_DEF_BOOL => define_var!(self, Bool), - OP_DEF_DATE => define_var!(self, Date), - OP_DEF_LIST => define_var!(self, List), - OP_DEF_MAP => define_var!(self, Map), - OP_DEF_STRUCT => define_var!(self, Struct), + OP_DEF_I32 => define_var!(self, I32, chunk), + OP_DEF_I64 => define_var!(self, I64, chunk), + OP_DEF_U32 => define_var!(self, U32, chunk), + OP_DEF_U64 => define_var!(self, U64, chunk), + OP_DEF_F32 => define_var!(self, F32, chunk), + OP_DEF_F64 => define_var!(self, F64, chunk), + OP_DEF_STRING => define_var!(self, String, chunk), + OP_DEF_CHAR => define_var!(self, Char, chunk), + OP_DEF_BOOL => define_var!(self, Bool, chunk), + OP_DEF_DATE => define_var!(self, Date, chunk), + OP_DEF_LIST => define_var!(self, List, chunk), + OP_DEF_MAP => define_var!(self, Map, chunk), + OP_DEF_STRUCT => define_var!(self, Struct, chunk), OP_GET => { - let name = self.read_constant(); + let name = self.read_name(chunk); let value = self.local_vars.get(&name).unwrap(); self.push(value.clone()); // not happy debug!("after get {:?}", self.stack); } + OP_CALL => { + let function_index = self.read(chunk); + let function = chunk.functions.get(function_index).unwrap(); + let mut args = vec![]; + let num_args = self.read(chunk); + for _ in 0..num_args { + let arg = self.pop(); + args.push(arg); + } + args.reverse(); + let result = interpret_function(function, args)?; + self.push(result); + } _ => {} } } } - fn read_constant(&mut self) -> String { - let name = self.chunk.constants[self.chunk.code[self.ip] as usize].to_string(); + fn read(&mut self, chunk: &Chunk) -> usize { self.ip += 1; - name + chunk.code[self.ip - 1] as usize + } + + fn read_name(&mut self, chunk: &Chunk) -> String { + let index = self.read(chunk); + chunk.constants[index].to_string() //string?? } fn reset_stack(&mut self) { @@ -177,8 +204,8 @@ pub const OP_DIVIDE: u16 = 5; pub const OP_NEGATE: u16 = 6; pub const OP_PRINT: u16 = 7; pub const OP_RETURN: u16 = 8; -// pub const OP_TRUE: u16 = 9; -// pub const OP_FALSE: u16 = 10; // obsolete, vacant space +pub const OP_CALL: u16 = 9; +pub const OP_DEF_FN: u16 = 10; pub const OP_AND: u16 = 11; pub const OP_OR: u16 = 12; pub const OP_NOT: u16 = 13;