~starkingdoms/starkingdoms

339f063faf61671754325b60bcad3ab02492e742 — ghostly_zsh 1 year, 4 months ago 314f853
print, add, sub run with manually typed bytecode
A kabel/opcodes.txt => kabel/opcodes.txt +7 -0
@@ 0,0 1,7 @@
CONSTANT VAL ; 0x00
ADD ; 0x01
SUB ; 0x02
MUL ; 0x03
DIV ; 0x04
MOD ; 0x05
PRINT ; 0xFF

M kabel/src/debug.rs => kabel/src/debug.rs +6 -0
@@ 97,6 97,12 @@ pub fn debug_ast(ast: AST, level: usize) -> String {
                output += &debug_ast(ast, level+1);
            }
        }
        // REMOVE LATER
        Print(expr) => {
            output += &"| ".repeat(level);
            output += "Print\n";
            output += &debug_ast(*expr, level+1);
        }
        Decl(name, expr) => {
            output += &"| ".repeat(level);
            output += "Decl ";

M kabel/src/lexer.rs => kabel/src/lexer.rs +3 -1
@@ 34,6 34,7 @@ impl Lexer {
        keywords.insert("var".to_string(), TokenType::Var);
        keywords.insert("true".to_string(), TokenType::True);
        keywords.insert("false".to_string(), TokenType::False);
        keywords.insert("print".to_string(), TokenType::Print);
        Self {
            input: input.chars().collect(),
            start: 0,


@@ 355,6 356,7 @@ pub enum TokenType {
    Var,
    True,
    False,
    Print,

    // characters
    Star,


@@ 406,7 408,7 @@ impl Display for TokenType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        use TokenType::*;
        token_display!(*self, f, Function, Return, Loop, While, For,
            Break, Continue, If, Else, Var, True, False,
            Break, Continue, If, Else, Var, True, False, Print,
                Star, StarEqual, Slash, SlashEqual, Percent, PercentEqual,
            Plus, PlusPlus, PlusEqual, Minus, MinusMinus, MinusEqual,
            LeftParen, RightParen, LeftBrace, RightBrace,

M kabel/src/lib.rs => kabel/src/lib.rs +2 -0
@@ 8,10 8,12 @@ use semantic_analysis::Analyzer;

pub mod debug;
pub mod error;
pub mod runtime_error;
pub mod lexer;
pub mod macros;
pub mod parser;
pub mod semantic_analysis;
pub mod opcodes;

pub fn run_lexer(input: String) -> Lexer {
    let mut lexer = Lexer::new(input);

M kabel/src/macros.rs => kabel/src/macros.rs +29 -1
@@ 96,7 96,7 @@ macro_rules! name {

#[macro_export]
macro_rules! push_output {
    ( $match_to:expr, $output:expr, $( $name:tt),*) => {
    ( $match_to:expr, $output:expr, $( $name:tt ),*) => {
        match $match_to {
            $( $name => { $output += stringify!($name); } ),*
        }


@@ 140,6 140,34 @@ macro_rules! out_of_scope_var {
        )
    };
}
#[macro_export]
macro_rules! mismatched_types {
    ($self:expr, $message:expr, $( $args:expr ),*) => {
        {
            let line = $self.find_line();
            $crate::runtime_error::KabelRuntimeError::new(
                $crate::runtime_error::RuntimeErrorKind::MismatchedTypes,
                format!($message, $( $args ),*),
                line-1,
                $self.text[line].to_string(),
            )
        }
    };
}
#[macro_export]
macro_rules! wrong_type {
    ($self:expr, $message:expr) => {
        {
            let line = $self.find_line();
            $crate::runtime_error::KabelRuntimeError::new(
                $crate::runtime_error::RuntimeErrorKind::WrongType,
                $message.to_string(),
                line-1,
                $self.text[line].to_string(),
            )
        }
    };
}

#[macro_export]
macro_rules! collect_lines {

A kabel/src/opcodes.rs => kabel/src/opcodes.rs +41 -0
@@ 0,0 1,41 @@
pub enum OpCode {
    CONSTANT,
    ADD,
    SUB,
    MUL,
    DIV,
    MOD,
    PRINT,
    ERR,
}

impl From<OpCode> for u8 {
    fn from(value: OpCode) -> Self {
        use OpCode::*;
        match value {
            CONSTANT => 0x00,
            ADD => 0x01,
            SUB => 0x02,
            MUL => 0x03,
            DIV => 0x04,
            MOD => 0x05,
            PRINT => 0xFE,
            ERR => 0xFF
        }
    }
}
impl From<u8> for OpCode {
    fn from(value: u8) -> Self {
        use OpCode::*;
        match value {
            0x00 => CONSTANT,
            0x01 => ADD,
            0x02 => SUB,
            0x03 => MUL,
            0x04 => DIV,
            0x05 => MOD,
            0xFE => PRINT,
            _ => ERR
        }
    }
}

M kabel/src/parser.rs => kabel/src/parser.rs +15 -0
@@ 52,6 52,7 @@ impl Parser {
            TokenType::Continue => self.continue_statement(),
            TokenType::If => self.if_statement(),
            TokenType::LeftBrace => self.block(),
            TokenType::Print => self.print_statement(), // REMOVE LATER
            _ => self.expression_statement(),
        }
    }


@@ 294,6 295,18 @@ impl Parser {
        }
    }

    // REMOVE LATER
    pub fn print_statement(&mut self) -> Result<AST, KabelError> {
        let print_ident = self.read_token()?;
        let expression = self.expression()?;
        let semicolon = self.read_token()?;
        if matches!(semicolon.token_type, TokenType::Semicolon) {
            Ok(ast_from_token!(ASTType::Print(Box::new(expression)), print_ident, semicolon))
        } else {
            Err(unexpected_token!(self, "Expected ; found {}", semicolon))
        }
    }

    pub fn expression_statement(&mut self) -> Result<AST, KabelError> {
        let expression = self.expression()?;
        if self.current >= self.input.len() {


@@ 1071,6 1084,8 @@ pub enum ASTType {
    Continue,
    If(Box<AST>, Box<AST>, Box<Option<AST>>), // condition, block, else/else if
    Block(Vec<AST>),                          // statements
    // REMOVE LATER
    Print(Box<AST>),

    // expressions
    Decl(Name, Box<AST>), // identifier, expression

A kabel/src/runtime_error.rs => kabel/src/runtime_error.rs +59 -0
@@ 0,0 1,59 @@
#[derive(Debug, Clone)]
pub struct KabelRuntimeError {
    pub kind: RuntimeErrorKind,
    pub message: String,
    pub line: usize,
    pub code: String,
}

impl KabelRuntimeError {
    pub fn new(kind: RuntimeErrorKind, message: String, line: usize, code: String) -> Self {
        Self {
            kind,
            message,
            line,
            code,
        }
    }
}

impl std::fmt::Display for KabelRuntimeError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(&format!(
            "Error {:0>4}: {1} at line {2}\n\
                    {3}\n",
            self.kind.clone() as usize,
            self.message,
            self.line + 1,
            self.code,
        ))
    }
}

impl std::error::Error for KabelRuntimeError {}

#[derive(Debug, Clone)]
pub enum RuntimeErrorKind {
    MismatchedTypes,
    WrongType,
}

impl std::fmt::Display for RuntimeErrorKind {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        use RuntimeErrorKind::*;
        match self {
            MismatchedTypes => f.write_str("Mismatched types"),
            WrongType => f.write_str("WrongType"),
        }
    }
}

impl From<RuntimeErrorKind> for usize {
    fn from(value: RuntimeErrorKind) -> Self {
        use RuntimeErrorKind::*;
        match value {
            MismatchedTypes => 0x06,
            WrongType => 0x07,
        }
    }
}

M kabel/src/semantic_analysis.rs => kabel/src/semantic_analysis.rs +8 -0
@@ 43,6 43,10 @@ impl Analyzer {
            Block(stmts) => {
                self.visit_block(stmts);
            }
            // REMOVE LATER
            Print(expr) => {
                self.visit_print(*expr);
            }
            Decl(name, expr) => {
                self.visit_decl(name, *expr);
            }


@@ 125,6 129,10 @@ impl Analyzer {
        }
        self.symbol_table.pop();
    }
    // REMOVE LATER
    pub fn visit_print(&mut self, expr: AST) {
        self.visit(expr);
    }
    pub fn visit_decl(&mut self, name: Name, expr: AST) {
        self.visit(expr);
        self.symbol_table.last_mut().unwrap().insert(name.name, Symbol::Var);

M kabel_test/src/lib.rs => kabel_test/src/lib.rs +2 -1
@@ 1,7 1,8 @@
use kabel::{debug::{debug_ast, debug_token_array}, run_lexer, run_parser, run_semantic_analysis};
use test_each_file::test_each_file;

mod main;
mod vm;
mod string;

test_each_file! { for ["kab", "out"] in "./kabel_test/test/runtime/" => test }
test_each_file! { for ["kab", "out"] in "./kabel_test/test/lexer/" => test_lexer }

M kabel_test/src/main.rs => kabel_test/src/main.rs +13 -4
@@ 1,9 1,15 @@
use std::{env, fs};
//use std::{env, fs};

use kabel::{debug::{debug_ast, debug_token_array}, run_lexer, run_parser, run_semantic_analysis};
//use kabel::{debug::{debug_ast, debug_token_array}, run_lexer, run_parser, run_semantic_analysis};

use vm::{Value, VM};

//use crate::vm::{Value, VM};
mod vm;
mod string;

fn main() {
    let args: Vec<String> = env::args().collect();
    /*let args: Vec<String> = env::args().collect();
    let program =
        fs::read_to_string(args[1].clone()).unwrap();



@@ 42,5 48,8 @@ fn main() {
        output += "\n";
    }

    println!("{}", output);
    println!("{}", output);*/
    let mut vm = VM::new(vec![0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x01, 0xFE], vec![(1,6)],
        vec![Value::Num(1.),Value::Num(2.)], "print 1-2+2".to_string());
    vm.run().unwrap();
}

A kabel_test/src/string.rs => kabel_test/src/string.rs +61 -0
@@ 0,0 1,61 @@
use std::{alloc::{self, Layout}, ops::Add};

#[derive(Debug, Clone, Copy)]
pub struct ObjString {
    pub ptr: *mut char,
    pub length: usize,
}

impl From<String> for ObjString {
    fn from(value: String) -> Self {
        unsafe {
            let layout = Layout::array::<char>(value.len()).unwrap();
            let ptr = alloc::alloc(layout) as *mut char;
            Self {
                ptr,
                length: value.len(),
            }
        }
    }
}
impl From<ObjString> for String {
    fn from(value: ObjString) -> Self {
        unsafe {
            String::from_raw_parts(value.ptr as *mut u8, value.length, value.length)
        }
    }
}
impl ToString for ObjString {
    fn to_string(&self) -> String {
        String::from(*self)
    }
}

impl Add for ObjString {
    type Output = Self;
    fn add(self, other: Self) -> Self {
        let layout = Layout::array::<char>(self.length + other.length).unwrap();
        unsafe {
            let ptr = alloc::alloc(layout) as *mut char;
            self.ptr.copy_to(ptr, self.length);
            other.ptr.copy_to(ptr.offset(self.length as isize), other.length);
            self.free();
            other.free();
            Self {
                ptr,
                length: self.length + other.length,
            }
        }
    }
}

impl ObjString {
    pub fn free(self) {
        if self.length != 0 {
            let layout = Layout::array::<char>(self.length).unwrap();
            unsafe {
                alloc::dealloc(self.ptr as *mut u8, layout)
            }
        }
    }
}

A kabel_test/src/vm.rs => kabel_test/src/vm.rs +137 -0
@@ 0,0 1,137 @@
use kabel::{mismatched_types, runtime_error::KabelRuntimeError, wrong_type};

use crate::string::ObjString;

pub struct VM {
    ip: usize,
    chunk: Vec<u8>,
    stack: Vec<Value>,
    pool: Vec<Value>,
    lines: Vec<(usize, usize)>, // line #, repeats number of instructions
    text: Vec<String>
}

impl VM {
    pub fn new(bytecode: Vec<u8>, lines: Vec<(usize, usize)>, pool: Vec<Value>, text: String) -> Self {
        Self {
            ip: 0,
            chunk: bytecode,
            stack: Vec::new(),
            pool,
            lines,
            text: text.lines().map(|s| s.to_string()).collect(),
        }
    }
    pub fn run(&mut self) -> Result<(), KabelRuntimeError> {
        use Value::*;
        while self.ip < self.chunk.len() {
            match self.read() {
                0x00 => { // CONSTANT
                    let byte = self.read() as usize;
                    self.stack.push(self.pool[byte])
                }
                0x01 => { // ADD
                    match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
                        (Num(v1), Num(v2)) => self.stack.push(Num(v1 + v2)),
                        (Str(v1), Str(v2)) => {
                            self.stack.push(Str(v1 + v2));
                            v1.free();
                            v2.free();
                        },
                        (Bool(_v1), Bool(_v2)) => {
                            return Err(wrong_type!(self, "Cannot add booleans"))
                        }
                        (v1, v2) => {
                            if let Str(v1) = v1 {
                                v1.free();
                            }
                            if let Str(v2) = v2 {
                                v2.free();
                            }
                            return Err(mismatched_types!(self, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                        }
                    }
                }
                0x02 => { // SUB
                    match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
                        (Num(v1), Num(v2)) => self.stack.push(Num(v1 - v2)),
                        (Str(v1), Str(v2)) => {
                            v1.free(); v2.free();
                            return Err(wrong_type!(self, "Cannot subtract strings"))
                        },
                        (Bool(_v1), Bool(_v2)) => {
                            return Err(wrong_type!(self, "Cannot subtract booleans"))
                        }
                        (v1, v2) => {
                            if let Str(v1) = v1 {
                                v1.free();
                            }
                            if let Str(v2) = v2 {
                                v2.free();
                            }
                            return Err(mismatched_types!(self, "Mismatched types: {} and {}", v1.type_str(), v2.type_str()))
                        }
                    }
                }

                0xFE => { // PRINT
                    let value = self.stack.pop().unwrap();
                    match value {
                        Num(v) => println!("{}", v),
                        Str(v) => println!("{}", v.to_string()),
                        Bool(v) => println!("{}", v),
                    }
                }
                _ => {}
            }
        }
        Ok(())
    }
    pub fn read(&mut self) -> u8 {
        let byte = self.chunk[self.ip];
        self.ip += 1;
        byte
    }
    pub fn find_line(&mut self) -> usize { // returns line # at ip
        let mut line_ip = 0;
        for (line, rep) in self.lines.clone() {
            if line_ip + rep > self.ip {
                return line;
            }
            line_ip += rep;
        }
        panic!("Something went wrong in finding line for error")
    }
}

impl Drop for VM {
    fn drop(&mut self) {
        for element in self.stack.clone() {
            match element {
                Value::Str(v1) => v1.free(),
                _ => {}
            }
        }
        for element in self.pool.clone() {
            match element {
                Value::Str(v1) => v1.free(),
                _ => {}
            }
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum Value {
    Num(f32), Str(ObjString), Bool(bool),
}

impl Value {
    pub fn type_str(&self) -> String {
        match self {
            Value::Num(_) => "number".to_string(),
            Value::Str(_) => "string".to_string(),
            Value::Bool(_) => "bool".to_string(),
        }
    }
}