From 37be9a1c18005a7cdc257178c7657638f5472101 Mon Sep 17 00:00:00 2001 From: ghostly_zsh Date: Wed, 21 Aug 2024 19:08:44 -0500 Subject: [PATCH] functions act like variables --- kabel/grammar.ebnf | 2 +- kabel/opcodes.txt | 4 ++-- kabel/src/ast.rs | 2 +- kabel/src/codegen.rs | 46 ++++++++++++++++++++++-------------- kabel/src/debug.rs | 9 +++---- kabel/src/main.rs | 4 ++-- kabel/src/name_resolution.rs | 30 ++++------------------- kabel/src/parser.rs | 2 +- kabel/src/runtime_error.rs | 5 +++- kabel/src/vm.rs | 37 ++++++++++++++++++++++------- kabel/tmp.kab | 2 +- 11 files changed, 77 insertions(+), 66 deletions(-) diff --git a/kabel/grammar.ebnf b/kabel/grammar.ebnf index 9959a1ad5950e8da4df495079ed39863768ebf72..12bb65033256bdfe332747c1ae589299596aa98b 100644 --- a/kabel/grammar.ebnf +++ b/kabel/grammar.ebnf @@ -53,7 +53,7 @@ array = "[" , { expression , "," ? } , "]" ; member = identifier , "." , { ( identifier | call ) , "." ? } ; -call = ( identifier , "(" , { expression, "," ? } , ")" ) ; +call = identifier , "(" , { expression, "," ? } , ")" ; incdec = identifier , [ "++" , "--" ] ; diff --git a/kabel/opcodes.txt b/kabel/opcodes.txt index ce10c02f6e5760136c1d943555f6b642b0826c63..4e77058590e696559691d046c2e1eae332a8b6b9 100644 --- a/kabel/opcodes.txt +++ b/kabel/opcodes.txt @@ -26,8 +26,8 @@ JMP LOC ; 0x15 JMP_UP LOC ; 0x16 JNE ELSE ; 0x17 -CALL NUMARGS UNIT_PTR ; 0x18 -RET ; 0x19 +CALL NUMARGS ; 0x18 +RET ; 0x19 POP COUNT ; 0xFD PRINT ; 0xFE diff --git a/kabel/src/ast.rs b/kabel/src/ast.rs index d44b6971bf55d90c73f8f1a6377009700d731f50..f269e9ecf77a72d34c68145332a8f066a6c0aba6 100644 --- a/kabel/src/ast.rs +++ b/kabel/src/ast.rs @@ -43,7 +43,7 @@ pub enum ASTType { // primary Lit(Lit), - Call(Name, Vec), + Call(Box, Vec), Member(Box, Box), } diff --git a/kabel/src/codegen.rs b/kabel/src/codegen.rs index 5e4ef26478a62a27fbf1a1a580a38c28af5b11b3..f716bb87bc191d772ef9e702fc3c482e8f2f7d7f 100644 --- a/kabel/src/codegen.rs +++ b/kabel/src/codegen.rs @@ -1,4 +1,4 @@ -use crate::{ast::{ASTType, BinOp, Lit, Name, UnOp, AST}, codegen_binary, codegen_unary, extension::Extension, opcodes::OpCode, vm::{Unit, Value, VM}}; +use crate::{ast::{ASTType, BinOp, Lit, Name, UnOp, AST}, codegen_binary, codegen_unary, extension::Extension, opcodes::OpCode, vm::{Function, Unit, Value, VM}}; pub struct Codegen { pub vm: VM, @@ -72,18 +72,34 @@ impl Codegen { self.visit_lit(&ast, lit.clone()); } Call(ref name, ref args) => { - self.visit_call(&ast, name.clone(), args.clone()); + self.visit_call(&ast, *name.clone(), args.clone()); } _ => {} } } - pub fn visit_function(&mut self, _name: Name, _args: Vec, block: AST) { - let old_unit_ptr = self.vm.unit_ptr; - let old_ip = self.vm.ip; + pub fn visit_function(&mut self, _name: Name, args: Vec, block: AST) { // create new compilation unit self.vm.units.push(Unit::new_empty()); + + // add function to constant pool and add a load expression in its place + 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: args.len(), + })); + 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; + } + + let old_unit_ptr = self.vm.unit_ptr; + let old_ip = self.vm.ip; // set unit to write to - self.vm.unit_ptr = self.vm.units.len()-1; + self.vm.unit_ptr = new_unit_ptr; self.vm.ip = 0; if let ASTType::Block(ref stmts) = block.ast_type { @@ -282,9 +298,6 @@ impl Codegen { } self.break_stack.last_mut().expect("break not in a loop").push(self.vm.units[self.vm.unit_ptr].code.len()-2); } - // ====================================================================== - // CONTINUE IS BROKEN AND HANGS IN FOR LOOPS, FIX LATER - // ====================================================================== pub fn visit_continue(&mut self, ast: &AST) { let mut scopes = self.scopes.clone(); let mut pop_count = 0; @@ -490,20 +503,17 @@ impl Codegen { _ => {} } } - pub fn visit_call(&mut self, ast: &AST, _name: Name, args: Vec) { + pub fn visit_call(&mut self, ast: &AST, ident: AST, args: Vec) { for arg in args.iter().rev() { self.visit(arg.clone()); } + self.visit(ident); self.vm.units[self.vm.unit_ptr].code.push(OpCode::CALL.into()); self.vm.units[self.vm.unit_ptr].code.push(args.len() as u8); - #[allow(irrefutable_let_patterns)] - if let Extension::Resolution(_scope, ptr) = ast.extensions[0] { - self.vm.units[self.vm.unit_ptr].code.push(ptr as u8); - if self.vm.units[self.vm.unit_ptr].lines.len() == 0 || self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line { - self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 3)); - } else { - self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 3; - } + if self.vm.units[self.vm.unit_ptr].lines.len() == 0 || self.vm.units[self.vm.unit_ptr].lines.last().unwrap().0 != ast.end_line { + self.vm.units[self.vm.unit_ptr].lines.push((ast.end_line, 2)); + } else { + self.vm.units[self.vm.unit_ptr].lines.last_mut().unwrap().1 += 2; } } pub fn patch_jump(&mut self, loc: usize, offset: i32) { diff --git a/kabel/src/debug.rs b/kabel/src/debug.rs index ae9c6168615ee6be7b06d615360374285475ee9d..54e27a8c84849b3bfaad3193a46c6ca123e9b95a 100644 --- a/kabel/src/debug.rs +++ b/kabel/src/debug.rs @@ -1,4 +1,4 @@ -use crate::{ast::AST, extension::Extension, lexer::Token, push_codegen, push_output, vm::{Value, VM}}; +use crate::{ast::AST, lexer::Token, push_codegen, push_output, vm::{Value, VM}}; pub fn debug_token_array(tokens: Vec) -> String { let mut output = "".to_string(); @@ -193,8 +193,8 @@ pub fn debug_ast(ast: AST, level: usize) -> String { } Call(name, args) => { output += &"| ".repeat(level); - output += "Call "; - output += &name.name; + output += "Call\n"; + output += &debug_ast(*name, level+1); for arg in args { output += "\n"; output += &debug_ast(arg, level+1); @@ -293,9 +293,6 @@ pub fn debug_bytecode(vm: &VM) -> String { output += " CALL "; vm.ip += 1; output += &(vm.units[vm.unit_ptr].code[vm.ip]).to_string(); - output += " "; - vm.ip += 1; - output += &(vm.units[vm.unit_ptr].code[vm.ip]).to_string(); output += "\n"; } RET => { diff --git a/kabel/src/main.rs b/kabel/src/main.rs index 9a71d653574333bce04c43ca746edd1211b6b8c8..dbf7fa8f96e44df4734f9bf51e893e6d674a689a 100644 --- a/kabel/src/main.rs +++ b/kabel/src/main.rs @@ -52,6 +52,7 @@ fn main() { let codegen = run_codegen(program.to_string(), ast); let mut vm = codegen.vm; + output += &debug_stack(&vm.units[0].stack); for (index, _) in vm.units.iter().enumerate() { vm.unit_ptr = index; output += "unit ptr: "; @@ -61,14 +62,13 @@ fn main() { output += "\n"; } vm.unit_ptr = 0; - println!("{}", output); output += "\n"; match vm.run(&mut output) { Ok(()) => {} Err(e) => output += &e.to_string(), } - output += &debug_stack(&vm.units[0].stack); + //output += &debug_stack(&vm.units[0].stack); println!("{}", output); } diff --git a/kabel/src/name_resolution.rs b/kabel/src/name_resolution.rs index 921688debc9ef9f7203370beb5e8253e9ce45969..1430fc31ae20549b00a443d4413629659c3216ca 100644 --- a/kabel/src/name_resolution.rs +++ b/kabel/src/name_resolution.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::{ast::{ASTType, AST}, ast_error, ast_from_ast, collect_lines, error::{ErrorKind, KabelError}, extension::Extension, out_of_scope_var}; +use crate::{ast::{ASTType, AST}, ast_error, ast_from_ast, error::{ErrorKind, KabelError}, extension::Extension, out_of_scope_var}; pub struct Resolver { text: Vec, @@ -260,34 +260,14 @@ impl Resolver { ast_from_ast!(Lit(lit), ast, ast) } Call(ident, args) => { - let resolution = self.resolve_function(&ident.name, args.len()); - let place = match resolution { - Ok(place) => place, - Err(e) => match e { - (ErrorKind::OutOfScope, _, _) => { self.errors.push(ast_error!(self, ErrorKind::OutOfScope, ast, "Function \"{}\" not in scope", ident.name)); 0 }, - (ErrorKind::IncorrectArity, Some(f_arity), Some(arity)) => { - self.errors.push( - KabelError::new( - ErrorKind::IncorrectArity, - format!("Function {} has {} argument, provided {}", ident.name, f_arity, arity), - None, - ast.start_line, - ast.start_column, - collect_lines!(self.text[ast.start_line-1..ast.end_line-1]), - ) - ); - 0 - } - _ => { panic!("Returned invalid ErrorKind from resolve_function") }, - } - }; + let ident = self.visit(*ident.clone()); let mut n_args = Vec::new(); for arg in args { n_args.push(self.visit(arg)); } return AST { - ast_type: Call(ident, n_args), - extensions: vec![Extension::Resolution(self.scope, place+1)], + ast_type: Call(Box::new(ident), n_args), + extensions: Vec::new(), start_line: ast.start_line, end_line: ast.end_line, start_column: ast.start_column, @@ -307,7 +287,7 @@ impl Resolver { }*/ fn resolve_var(&self, name: &String) -> (bool, usize) { for scope in self.symbol_table.iter().rev() { - if let Some((Symbol::Var, place)) = scope.get(name) { + if let Some((Symbol::Var, place)) | Some((Symbol::Function(_), place)) = scope.get(name) { return (true, *place); } } diff --git a/kabel/src/parser.rs b/kabel/src/parser.rs index a4ef97e21287a667e1c9a886b8d209cc8b2b5ea9..7f264d81cdb99ad6b03db58dc5d86b07fd01c112 100644 --- a/kabel/src/parser.rs +++ b/kabel/src/parser.rs @@ -978,7 +978,7 @@ impl Parser { let right_paren = self.read_token()?; if let TokenType::Ident(name) = ident.token_type { return Ok(ast_from_token!( - ASTType::Call(name!(name, ident), expressions), + ASTType::Call(Box::new(lit!(Ident, name, ident)), expressions), ident, right_paren )); diff --git a/kabel/src/runtime_error.rs b/kabel/src/runtime_error.rs index b143a9ab158b37744561a5a781828d891b11686d..28dbbd9c477f2338c892fae76f57ee93b479450b 100644 --- a/kabel/src/runtime_error.rs +++ b/kabel/src/runtime_error.rs @@ -42,6 +42,7 @@ impl std::error::Error for KabelRuntimeError {} pub enum RuntimeErrorKind { MismatchedTypes, WrongType, + IncorrectArity, } impl std::fmt::Display for RuntimeErrorKind { @@ -49,7 +50,8 @@ impl std::fmt::Display for RuntimeErrorKind { use RuntimeErrorKind::*; match self { MismatchedTypes => f.write_str("Mismatched types"), - WrongType => f.write_str("WrongType"), + WrongType => f.write_str("Wrong type"), + IncorrectArity => f.write_str("Incorrect arity"), } } } @@ -60,6 +62,7 @@ impl From for usize { match value { MismatchedTypes => 0x00, WrongType => 0x01, + IncorrectArity => 0x02, } } } diff --git a/kabel/src/vm.rs b/kabel/src/vm.rs index 5861c6d698267fe1c5d961ac293085228ca212ee..73ed4f16a570a303d3c2e05d7d070ca5f2460457 100644 --- a/kabel/src/vm.rs +++ b/kabel/src/vm.rs @@ -264,6 +264,9 @@ impl VM { return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-boolean string")) } Bool(v1) => self.units[self.unit_ptr].stack.push(Bool(!v1)), + Fun(_v1) => { + return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"!\" on non-boolean function")) + } } // NEG 0x14 => match self.units[self.unit_ptr].stack.pop().unwrap() { @@ -275,6 +278,9 @@ impl VM { Bool(_v1) => { return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"-\" on non-number boolean")) } + Fun(_v1) => { + return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to perform \"-\" on non-number function")) + } } // JMP 0x15 => { @@ -304,14 +310,21 @@ impl VM { // CALL 0x18 => { let num_args = self.read(); - let unit_ptr = self.read(); - self.call_stack.push((self.unit_ptr, self.ip)); - for _ in 0..num_args { - let arg = self.units[self.unit_ptr].stack.pop().expect("Missing arguments in call"); - self.units[unit_ptr as usize].stack.push(arg); + let function = self.units[self.unit_ptr].stack.pop().expect("Stack was empty in call"); + if let Value::Fun(function) = function { + if num_args as usize != function.arity { + return Err(vm_error!(self, RuntimeErrorKind::IncorrectArity, "Function has {} arguments, {} provided", function.arity, num_args)) + } + self.call_stack.push((self.unit_ptr, self.ip)); + for _ in 0..num_args { + let arg = self.units[self.unit_ptr].stack.pop().expect("Missing arguments in call"); + self.units[function.unit_ptr as usize].stack.push(arg); + } + self.ip = 0; + self.unit_ptr = function.unit_ptr as usize; + } else { + return Err(vm_error!(self, RuntimeErrorKind::WrongType, "Tried to call non-function type {}", function.type_str())) } - self.ip = 0; - self.unit_ptr = unit_ptr as usize; } // RET 0x19 => { @@ -337,6 +350,7 @@ impl VM { Num(v) => *output += &v.to_string(), Str(v) => *output += &v, Bool(v) => *output += &v.to_string(), + Fun(v) => *output += &format!("", v.unit_ptr), } *output += "\n"; } @@ -372,7 +386,7 @@ impl VM { #[derive(Debug, Clone)] pub enum Value { - Null, Num(f32), Str(String), Bool(bool), + Null, Num(f32), Str(String), Bool(bool), Fun(Function) } impl Value { @@ -382,6 +396,13 @@ impl Value { Value::Num(_) => "number".to_string(), Value::Str(_) => "string".to_string(), Value::Bool(_) => "bool".to_string(), + Value::Fun(_) => "function".to_string(), } } } + +#[derive(Debug, Clone)] +pub struct Function { + pub unit_ptr: usize, + pub arity: usize, +} diff --git a/kabel/tmp.kab b/kabel/tmp.kab index 2bd4c07e7c27fce1764eefa512207c5645f3951d..1b7fb356a4a142a89ac542937e42b76e8a7dd380 100644 --- a/kabel/tmp.kab +++ b/kabel/tmp.kab @@ -5,4 +5,4 @@ function bar(i) { function foo(i) { return i+1; } -print foo(2); +print foo(bar(k));