From fb87d2ae4d216d82afd37a99baec64f034d87f69 Mon Sep 17 00:00:00 2001 From: ghostly_zsh Date: Tue, 27 Aug 2024 13:04:11 -0500 Subject: [PATCH] anonymous functions --- .gitignore | 3 +- kabel/grammar.ebnf | 12 +++--- kabel/opcodes.txt | 2 +- kabel/src/ast.rs | 1 + kabel/src/codegen.rs | 67 +++++++++++++++++++++++++++++++ kabel/src/debug.rs | 15 +++++-- kabel/src/lexer.rs | 7 +++- kabel/src/name_resolution.rs | 14 +++++++ kabel/src/parser.rs | 78 +++++++++++++++++++++++++++++++++++- kabel/src/vm.rs | 7 +--- kabel/tmp.kab | 14 +++---- 11 files changed, 194 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 6c73a128751703abb29136ceeb7db42d54e2b7a3..243a5460fbe5f21274829e2b803d8d9520a3325b 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ starkingdoms-client/node_modules starkingdoms-client/dist client/node_modules starkingdoms-backplane/config.toml -starkingdoms-api/config.toml \ No newline at end of file +starkingdoms-api/config.toml +kabel/tmp.kab diff --git a/kabel/grammar.ebnf b/kabel/grammar.ebnf index 12bb65033256bdfe332747c1ae589299596aa98b..496d3d66b01a9b13528c5d3531a3b679e7c94249 100644 --- a/kabel/grammar.ebnf +++ b/kabel/grammar.ebnf @@ -1,7 +1,7 @@ program = { statement } ; statement = function | return | loop | while | for | break | continue - | if | block | expression_statement ; + | if | block | declaration | expression_statement ; function = "function" , identifier , "(" , { identifier , "," , } ")" , block ; return = "return" , expression , ";" ; @@ -16,14 +16,16 @@ if = "if" , "(" , expression , ")" , block [ , "else" , ( if | block ) ] ; block = "{" , { statement } , "}" ; -expression_statement = expression , ";" ; +declaration = "var" , identifier , "=" , expression , ";" ; -expression = assignment | declaration ; +expression_statement = expression , ";" ; -declaration = "var" , identifier , "=" , expression ; +expression = assignment ; assignment = ( identifier , ( "=" | "+=" | "-=" | "*=" - | "/=" | "%=" | "&=" | "^=" | "|=" ) , assignment ) | ternary ; + | "/=" | "%=" | "&=" | "^=" | "|=" ) , assignment ) | anonymous_function ; + +anonymous_function = ( "(" , { identifier , "," , } ")" , "=>" , block ) | ternary ; ternary = logical_or [ , "?" , expression , ":" , expression ] ; diff --git a/kabel/opcodes.txt b/kabel/opcodes.txt index dbd3c59e34d7ce9a7eed419391c5a920590da44e..89196d4deccbf4e4abf613d230defd824fabe056 100644 --- a/kabel/opcodes.txt +++ b/kabel/opcodes.txt @@ -26,7 +26,7 @@ JMP LOC ; 0x15 JMP_UP LOC ; 0x16 JNE ELSE ; 0x17 -CALL NUMARGS ; 0x18 +CALL ARITY ; 0x18 RET ; 0x19 LIST LEN ; 0x1A diff --git a/kabel/src/ast.rs b/kabel/src/ast.rs index f269e9ecf77a72d34c68145332a8f066a6c0aba6..92f42d3f570f7f11079de797d60dd604d6837975 100644 --- a/kabel/src/ast.rs +++ b/kabel/src/ast.rs @@ -36,6 +36,7 @@ pub enum ASTType { // expressions Assign(Name, Box), + Anonymous(Vec, Box), Ternary(Box, Box, Box), Subscript(Box, Box), Binary(Box, BinOp, Box), diff --git a/kabel/src/codegen.rs b/kabel/src/codegen.rs index 3bcb98552d9e6521c554e58be3efe476298cee2c..b0593c716aba047fc3ba61e5e027c659e01c181d 100644 --- a/kabel/src/codegen.rs +++ b/kabel/src/codegen.rs @@ -62,6 +62,9 @@ impl Codegen { Assign(ref name, ref expr) => { self.visit_assign(&ast, name.clone(), *expr.clone()); } + Anonymous(params, block) => { + self.visit_anonymous(params, *block); + } Subscript(ref left, ref right) => { self.visit_subscript(&ast, *left.clone(), *right.clone()); } @@ -448,6 +451,70 @@ impl Codegen { self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 1; } } + pub fn visit_anonymous(&mut self, params: Vec, block: AST) { + self.vm.units.push(Unit::new_empty()); + + let new_unit_ptr = self.vm.units.len()-1; + self.vm.units[self.vm.unit_ptr].pool.push(Value::Fun(Function { + unit_ptr: new_unit_ptr, + arity: params.len(), + })); + // load function to stack + self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into()); + let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8; + self.vm.units[self.vm.unit_ptr].code.push(loc); + if self.vm.units[self.vm.unit_ptr].lines.len() == 0 || self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line { + self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 2)); + } else { + self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2; + } + self.scopes.last_mut().expect("codegen scopes vec was empty").1 += 1; + + // enter function unit + let old_unit_ptr = self.vm.unit_ptr; + let old_ip = self.vm.ip; + self.vm.unit_ptr = new_unit_ptr; + self.vm.ip = 0; + + // update scopes with number of parameters + self.scopes.last_mut().expect("codegen scopes vec was empty").1 += params.len(); + + if let ASTType::Block(ref stmts) = block.ast_type { + self.visit_block(&block, stmts.clone(), ScopeType::Function); + } + // add implicit RET + match self.vm.units[self.vm.unit_ptr].code.last() { + Some(instr) => { + if *instr != >::into(OpCode::RET) { + self.vm.units[self.vm.unit_ptr].pool.push(Value::Null); + self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into()); + let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8; + self.vm.units[self.vm.unit_ptr].code.push(loc); + self.vm.units[self.vm.unit_ptr].code.push(OpCode::RET.into()); + if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line { + self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 3)); + } else { + self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3; + } + } + } + None => { + self.vm.units[self.vm.unit_ptr].pool.push(Value::Null); + self.vm.units[self.vm.unit_ptr].code.push(OpCode::LOAD.into()); + let loc = (self.vm.units[self.vm.unit_ptr].pool.len()-1) as u8; + self.vm.units[self.vm.unit_ptr].code.push(loc); + self.vm.units[self.vm.unit_ptr].code.push(OpCode::RET.into()); + if self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != block.end_line { + self.vm.units[self.vm.unit_ptr].lines.push((block.end_line, 3)); + } else { + self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3; + } + } + }; + + self.vm.unit_ptr = old_unit_ptr; + self.vm.ip = old_ip; + } pub fn visit_subscript(&mut self, ast: &AST, left: AST, right: AST) { self.visit(left); self.visit(right); diff --git a/kabel/src/debug.rs b/kabel/src/debug.rs index a150b509cfa4ed6a84d97286c2fc9e84d557921c..22dea802a0ae4aeae9d8c0a5fb9ca16a33b2981b 100644 --- a/kabel/src/debug.rs +++ b/kabel/src/debug.rs @@ -23,12 +23,12 @@ pub fn debug_ast(ast: AST, level: usize) -> String { output += &debug_ast(ast, level+1); } } - Function(name, args, block) => { + Function(name, params, block) => { output += &"| ".repeat(level); output += "Function"; output += &(" ".to_string() + &name.name); - for arg in args { - output += &(" ".to_string() + &arg.name); + for param in params { + output += &(" ".to_string() + ¶m.name); } output += &format!(" {:?}", ast.extensions); output += "\n"; @@ -131,6 +131,15 @@ pub fn debug_ast(ast: AST, level: usize) -> String { output += "\n"; output += &debug_ast(*expr, level+1); } + Anonymous(params, block) => { + output += &"| ".repeat(level); + output += "Anonymous "; + for param in params { + output += &(" ".to_string() + ¶m.name); + } + output += "\n"; + output += &debug_ast(*block, level+1); + } Ternary(condition, true_expr, false_expr) => { output += &"| ".repeat(level); output += "Ternary\n"; diff --git a/kabel/src/lexer.rs b/kabel/src/lexer.rs index 1caca511f2a6b50bc28045fc643433d41dfc6233..bd8c1babb4176939ea5bf623422a64bf2dc114df 100644 --- a/kabel/src/lexer.rs +++ b/kabel/src/lexer.rs @@ -213,6 +213,10 @@ impl Lexer { self.read_char("Kabel broke")?; self.output.push(token!(self, TokenType::EqualEqual)); self.start = self.line_current; + } else if self.peek("").unwrap_or(' ') == '>' { + self.read_char("Kabel broke")?; + self.output.push(token!(self, TokenType::Arrow)); + self.start = self.line_current; } else { self.output.push(token!(self, TokenType::Equal)); self.start = self.line_current; @@ -415,6 +419,7 @@ pub enum TokenType { Semicolon, Colon, Question, + Arrow, Ident(String), Str(String), @@ -432,7 +437,7 @@ impl Display for TokenType { LeftSquare, RightSquare, Equal, EqualEqual, Bang, BangEqual, Greater, GreaterEqual, Less, LessEqual, And, AndEqual, AndAnd, Or, OrEqual, OrOr, - Caret, CaretEqual, Period, Comma, Semicolon, Colon, Question); + Caret, CaretEqual, Period, Comma, Semicolon, Colon, Question, Arrow); match *self { Ident(ref name) => { f.write_str("Ident ")?; diff --git a/kabel/src/name_resolution.rs b/kabel/src/name_resolution.rs index de5eeab9cca225e5864e2f14803571f2b399de0c..8444dd537b6b9923cbc1b6ab069400dc571e0331 100644 --- a/kabel/src/name_resolution.rs +++ b/kabel/src/name_resolution.rs @@ -223,6 +223,19 @@ impl Resolver { end_column: ast.end_column, } } + Anonymous(params, block) => { + self.locals.push(Vec::new()); + self.symbol_table.push(HashMap::new()); + self.locals.last_mut().expect("locals last in anonymous self-reference push").push(self.scope); + for param in params.clone() { + self.locals.last_mut().expect("locals last in anonymous param push").push(self.scope+1); + self.symbol_table.last_mut().unwrap().insert(param.name, (Symbol::Var,self.locals.last().expect("locals last in anonymous param len").len()-1)); + } + let block = self.visit(*block); + self.symbol_table.pop(); + self.locals.pop(); + ast_from_ast!(Anonymous(params, Box::new(block)), ast, ast) + } Ternary(condition, true_expr, false_expr) => { let condition = self.visit(*condition); let true_expr = self.visit(*true_expr); @@ -317,4 +330,5 @@ impl Resolver { pub enum Symbol { Var, Function(usize), + Anonymous(usize), } diff --git a/kabel/src/parser.rs b/kabel/src/parser.rs index 7f264d81cdb99ad6b03db58dc5d86b07fd01c112..6b4a461115724c4c6d910f3660dc70117d5689d3 100644 --- a/kabel/src/parser.rs +++ b/kabel/src/parser.rs @@ -398,7 +398,7 @@ impl Parser { let ident = self.read_token()?; if self.current >= self.input.len() { self.current -= 1; - return self.logical_or(); + return self.ternary(); } if let TokenType::Equal | TokenType::PlusEqual @@ -584,9 +584,83 @@ impl Parser { } } self.current -= 1; - return self.ternary(); + return self.anonymous_function(); } + return self.anonymous_function(); + } + + pub fn anonymous_function(&mut self) -> Result { + if let TokenType::LeftParen = self.peek()?.token_type { + let left_paren = self.read_token()?; + match self.peek()?.token_type { + TokenType::Ident(name) => { + let param = self.read_token()?; + if matches!(self.peek()?.token_type, TokenType::Comma) { + self.read_token()?; + let mut params = vec![name!(name, param)]; + while self.peek()?.token_type != TokenType::RightParen { + let ident = self.read_token()?; + if let TokenType::Ident(name) = ident.token_type { + params.push(name!(name, ident)); + } else { + return Err(unexpected_token!(self, "Expected identifier but found {}", ident)); + } + if let TokenType::Comma = self.peek()?.token_type { + self.read_token()?; + } + } + self.read_token()?; // right paren + if let TokenType::Arrow = self.peek()?.token_type { + self.read_token()?; + if let TokenType::LeftBrace = self.peek()?.token_type { + let block = self.block()?; + return Ok(ast_from_token_ast!(ASTType::Anonymous(params, Box::new(block.clone())), left_paren, block)); + } else { + let left_brace = self.read_token()?; + return Err(unexpected_token!(self, "Expected {{ but found {}", left_brace)); + } + } else { + let arrow = self.read_token()?; + return Err(unexpected_token!(self, "Expected => but found {}", arrow)); + } + } else if matches!(self.peek()?.token_type, TokenType::RightParen) { + self.read_token()?; // right paren + let params = vec![name!(name, param)]; + if let TokenType::Arrow = self.peek()?.token_type { + self.read_token()?; + if let TokenType::LeftBrace = self.peek()?.token_type { + let block = self.block()?; + return Ok(ast_from_token_ast!(ASTType::Anonymous(params, Box::new(block.clone())), left_paren, block)); + } else { + let left_brace = self.read_token()?; + return Err(unexpected_token!(self, "Expected {{ but found {}", left_brace)); + } + } else { + let arrow = self.read_token()?; + return Err(unexpected_token!(self, "Expected => but found {}", arrow)); + } + } else { + self.current -= 1; + return self.group(left_paren); + } + } + TokenType::RightParen => { + self.read_token()?; // right paren + if let TokenType::Arrow = self.peek()?.token_type { + self.read_token()?; + let block = self.block()?; + return Ok(ast_from_token_ast!(ASTType::Anonymous(Vec::new(), Box::new(block.clone())), left_paren, block)); + } else { + let arrow = self.read_token()?; + return Err(unexpected_token!(self, "Expected => but found {}", arrow)); + } + } + _ => { + return self.group(left_paren); + } + } + } return self.ternary(); } diff --git a/kabel/src/vm.rs b/kabel/src/vm.rs index 9ed5efa3af77c9d6bdb01827e91937898e88b13e..de068a26aed3d5f782de65a5a3a34ba7c142de27 100644 --- a/kabel/src/vm.rs +++ b/kabel/src/vm.rs @@ -29,7 +29,6 @@ impl Unit { pub struct VM { pub ip: usize, pub unit_ptr: usize, - pub stack_ptr: usize, pub call_stack: Vec<(usize, usize, usize)>, // (unit_ptr, ip, stack_offset) pub units: Vec, pub stack: Vec, @@ -43,7 +42,6 @@ impl VM { Self { ip: 0, unit_ptr: 0, - stack_ptr: 0, call_stack: vec![(0, 0, 0)], units: vec![Unit::new(bytecode, pool, lines)], stack: Vec::new(), @@ -334,8 +332,6 @@ impl VM { self.stack.push(arg); } self.stack.insert(self.stack.len()-num_args as usize, Value::Fun(function)); - //println!("stack call {:?}", self.stack); - self.stack_ptr += 1; self.ip = 0; self.unit_ptr = function.unit_ptr as usize; } else { @@ -351,9 +347,9 @@ impl VM { self.unit_ptr = unit_ptr; self.ip = ip; // returned to code - self.stack_ptr -= 1; self.stack.push(ret); } + // LIST 0x1A => { let len = self.read(); @@ -432,7 +428,6 @@ impl VM { } line_ip += rep; } - println!("{} {}", line_ip, self.ip); panic!("Something went wrong in finding line for error") } } diff --git a/kabel/tmp.kab b/kabel/tmp.kab index 9831809e6da0588be8c2d07567676aa9e61d8ad7..330ab7b0b84aab79d4b539194a8015e3dfb0ecf8 100644 --- a/kabel/tmp.kab +++ b/kabel/tmp.kab @@ -1,8 +1,8 @@ -function foo(bar) { - bar(); +var add_one = (num) => { + return num + 1; +}; +function add_two(num) { + return num + 2; } -function bar() { - print 3; -} - -foo(bar); +print add_one(1); +print add_two(2);