~starkingdoms/starkingdoms

37be9a1c18005a7cdc257178c7657638f5472101 — ghostly_zsh 1 year, 3 months ago 2ee9958
functions act like variables
M kabel/grammar.ebnf => kabel/grammar.ebnf +1 -1
@@ 53,7 53,7 @@ array = "[" , { expression , "," ? } , "]" ;

member = identifier , "." ,  { ( identifier | call ) , "." ? } ;

call = ( identifier , "(" , { expression, "," ? } , ")" ) ;
call = identifier , "(" , { expression, "," ? } , ")" ;

incdec = identifier , [ "++" , "--" ] ;


M kabel/opcodes.txt => kabel/opcodes.txt +2 -2
@@ 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

M kabel/src/ast.rs => kabel/src/ast.rs +1 -1
@@ 43,7 43,7 @@ pub enum ASTType {

    // primary
    Lit(Lit),
    Call(Name, Vec<AST>),
    Call(Box<AST>, Vec<AST>),
    Member(Box<AST>, Box<AST>),
}


M kabel/src/codegen.rs => kabel/src/codegen.rs +28 -18
@@ 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<Name>, 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<Name>, 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<AST>) {
    pub fn visit_call(&mut self, ast: &AST, ident: AST, args: Vec<AST>) {
        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) {

M kabel/src/debug.rs => kabel/src/debug.rs +3 -6
@@ 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<Token>) -> 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 => {

M kabel/src/main.rs => kabel/src/main.rs +2 -2
@@ 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);
}

M kabel/src/name_resolution.rs => kabel/src/name_resolution.rs +5 -25
@@ 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<String>,


@@ 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);
            }
        }

M kabel/src/parser.rs => kabel/src/parser.rs +1 -1
@@ 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
            ));

M kabel/src/runtime_error.rs => kabel/src/runtime_error.rs +4 -1
@@ 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<RuntimeErrorKind> for usize {
        match value {
            MismatchedTypes => 0x00,
            WrongType => 0x01,
            IncorrectArity => 0x02,
        }
    }
}

M kabel/src/vm.rs => kabel/src/vm.rs +29 -8
@@ 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!("<function {}>", 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,
}

M kabel/tmp.kab => kabel/tmp.kab +1 -1
@@ 5,4 5,4 @@ function bar(i) {
function foo(i) {
    return i+1;
}
print foo(2);
print foo(bar(k));