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(),
+ }
+ }
+}