From 339f063faf61671754325b60bcad3ab02492e742 Mon Sep 17 00:00:00 2001 From: ghostly_zsh Date: Thu, 8 Aug 2024 15:54:50 -0500 Subject: [PATCH] print, add, sub run with manually typed bytecode --- kabel/opcodes.txt | 7 ++ kabel/src/debug.rs | 6 ++ kabel/src/lexer.rs | 4 +- kabel/src/lib.rs | 2 + kabel/src/macros.rs | 30 +++++++- kabel/src/opcodes.rs | 41 ++++++++++ kabel/src/parser.rs | 15 ++++ kabel/src/runtime_error.rs | 59 ++++++++++++++ kabel/src/semantic_analysis.rs | 8 ++ kabel_test/src/lib.rs | 3 +- kabel_test/src/main.rs | 17 +++- kabel_test/src/string.rs | 61 +++++++++++++++ kabel_test/src/vm.rs | 137 +++++++++++++++++++++++++++++++++ 13 files changed, 383 insertions(+), 7 deletions(-) create mode 100644 kabel/opcodes.txt create mode 100644 kabel/src/opcodes.rs create mode 100644 kabel/src/runtime_error.rs create mode 100644 kabel_test/src/string.rs create mode 100644 kabel_test/src/vm.rs diff --git a/kabel/opcodes.txt b/kabel/opcodes.txt new file mode 100644 index 0000000000000000000000000000000000000000..b4e738d2978a7501008df8e890a0714268c6eb7c --- /dev/null +++ b/kabel/opcodes.txt @@ -0,0 +1,7 @@ +CONSTANT VAL ; 0x00 +ADD ; 0x01 +SUB ; 0x02 +MUL ; 0x03 +DIV ; 0x04 +MOD ; 0x05 +PRINT ; 0xFF diff --git a/kabel/src/debug.rs b/kabel/src/debug.rs index 707d9d5290212bf482d69b6f1dced2075bea78f6..8fde8878d109ef44b8f2516668ff565b0fbb79b6 100644 --- a/kabel/src/debug.rs +++ b/kabel/src/debug.rs @@ -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 "; diff --git a/kabel/src/lexer.rs b/kabel/src/lexer.rs index 2c38a7b8e30affd0a434cda619e89aa74352edf2..c7ebdd17e97332a9e64ced189394995b5cb6f58b 100644 --- a/kabel/src/lexer.rs +++ b/kabel/src/lexer.rs @@ -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, diff --git a/kabel/src/lib.rs b/kabel/src/lib.rs index 8c8fe75e019f0451dc3cdb3d285b98a21b87448c..80a11c2249b56dc7a163a7da378ee5b7a09d34db 100644 --- a/kabel/src/lib.rs +++ b/kabel/src/lib.rs @@ -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); diff --git a/kabel/src/macros.rs b/kabel/src/macros.rs index af554e0982bb60fb6bd18d58537bfb18d1830d6d..5a1d0b0d4f412a27a6ff116f5fd4abafbfca0b86 100644 --- a/kabel/src/macros.rs +++ b/kabel/src/macros.rs @@ -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 { diff --git a/kabel/src/opcodes.rs b/kabel/src/opcodes.rs new file mode 100644 index 0000000000000000000000000000000000000000..f62e1652c80ea217ee6351e97bc13061e6bc5cba --- /dev/null +++ b/kabel/src/opcodes.rs @@ -0,0 +1,41 @@ +pub enum OpCode { + CONSTANT, + ADD, + SUB, + MUL, + DIV, + MOD, + PRINT, + ERR, +} + +impl From 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 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 + } + } +} diff --git a/kabel/src/parser.rs b/kabel/src/parser.rs index 3310d5248307ca62df6ec785f46724402aab423e..dffb2588dfa1f38d67dc6ebceee72d9785fa9668 100644 --- a/kabel/src/parser.rs +++ b/kabel/src/parser.rs @@ -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 { + 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 { let expression = self.expression()?; if self.current >= self.input.len() { @@ -1071,6 +1084,8 @@ pub enum ASTType { Continue, If(Box, Box, Box>), // condition, block, else/else if Block(Vec), // statements + // REMOVE LATER + Print(Box), // expressions Decl(Name, Box), // identifier, expression diff --git a/kabel/src/runtime_error.rs b/kabel/src/runtime_error.rs new file mode 100644 index 0000000000000000000000000000000000000000..3990d465c82caf109657b3899c1c83b0e68a1ea4 --- /dev/null +++ b/kabel/src/runtime_error.rs @@ -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 for usize { + fn from(value: RuntimeErrorKind) -> Self { + use RuntimeErrorKind::*; + match value { + MismatchedTypes => 0x06, + WrongType => 0x07, + } + } +} diff --git a/kabel/src/semantic_analysis.rs b/kabel/src/semantic_analysis.rs index 3e1d07509e792f43b411f1f632dbdb23e4347360..45e6d39d59398741801f5ec55ea339da3c6914b9 100644 --- a/kabel/src/semantic_analysis.rs +++ b/kabel/src/semantic_analysis.rs @@ -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); diff --git a/kabel_test/src/lib.rs b/kabel_test/src/lib.rs index 9909c249435cc15b54c799d8917570acb48aa902..53951f25ac79b4b0ad624d8a83073ce3d40c4d7f 100644 --- a/kabel_test/src/lib.rs +++ b/kabel_test/src/lib.rs @@ -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 } diff --git a/kabel_test/src/main.rs b/kabel_test/src/main.rs index 4b2f63118435800200acf026317e9fcc873a42ff..8c33d5a617af6c4446cd7c90f3db96b927a794a0 100644 --- a/kabel_test/src/main.rs +++ b/kabel_test/src/main.rs @@ -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 = env::args().collect(); + /*let args: Vec = 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(); } diff --git a/kabel_test/src/string.rs b/kabel_test/src/string.rs new file mode 100644 index 0000000000000000000000000000000000000000..a3504b142ee421f6100de22950dfa1f9af5fdb3d --- /dev/null +++ b/kabel_test/src/string.rs @@ -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 for ObjString { + fn from(value: String) -> Self { + unsafe { + let layout = Layout::array::(value.len()).unwrap(); + let ptr = alloc::alloc(layout) as *mut char; + Self { + ptr, + length: value.len(), + } + } + } +} +impl From 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::(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::(self.length).unwrap(); + unsafe { + alloc::dealloc(self.ptr as *mut u8, layout) + } + } + } +} diff --git a/kabel_test/src/vm.rs b/kabel_test/src/vm.rs new file mode 100644 index 0000000000000000000000000000000000000000..376438af0cdfc779069cba04583961c95dbd74db --- /dev/null +++ b/kabel_test/src/vm.rs @@ -0,0 +1,137 @@ +use kabel::{mismatched_types, runtime_error::KabelRuntimeError, wrong_type}; + +use crate::string::ObjString; + +pub struct VM { + ip: usize, + chunk: Vec, + stack: Vec, + pool: Vec, + lines: Vec<(usize, usize)>, // line #, repeats number of instructions + text: Vec +} + +impl VM { + pub fn new(bytecode: Vec, lines: Vec<(usize, usize)>, pool: Vec, 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(), + } + } +}