M kabel/opcodes.txt => kabel/opcodes.txt +25 -22
@@ 1,25 1,28 @@
-CONSTANT VAL ; 0x00
-ADD ; 0x01
-SUB ; 0x02
-MUL ; 0x03
-DIV ; 0x04
-MOD ; 0x05
-BITAND ; 0x06
-BITXOR ; 0x07
-BITOR ; 0x08
-EQ ; 0x09
-NE ; 0x0A
-GR ; 0x0B
-GE ; 0x0C
-LS ; 0x0D
-LE ; 0x0E
-OR ; 0x0F
-AND ; 0x10
+CONSTANT VAL ; 0x00
+VAR STACKPTR ; 0x01
-NOT ; 0x11
-NEG ; 0x12
+ADD ; 0x02
+SUB ; 0x03
+MUL ; 0x04
+DIV ; 0x05
+MOD ; 0x06
+BITAND ; 0x07
+BITXOR ; 0x08
+BITOR ; 0x09
+EQ ; 0x0A
+NE ; 0x0B
+GR ; 0x0C
+GE ; 0x0D
+LS ; 0x0E
+LE ; 0x0F
+OR ; 0x10
+AND ; 0x11
-JMP LOC ; 0x13
-IF_NE ELSE ; 0x14
+NOT ; 0x12
+NEG ; 0x13
-PRINT ; 0xFF
+JMP LOC ; 0x14
+IF_NE ELSE ; 0x15
+
+POP ; 0xFD
+PRINT ; 0xFE
A kabel/src/ast.rs => kabel/src/ast.rs +91 -0
@@ 0,0 1,91 @@
+use crate::extension::Extension;
+
+#[derive(Debug, Clone)]
+pub struct AST {
+ pub ast_type: ASTType,
+ pub extensions: Vec<Extension>,
+ pub start_line: usize,
+ pub end_line: usize,
+ pub start_column: usize,
+ pub end_column: usize,
+}
+
+#[derive(Debug, Clone)]
+pub enum ASTType {
+ Program(Vec<AST>),
+
+ // statements
+ Function(Name, Vec<Name>, Box<AST>), // name, args, block
+ Return(Box<Option<AST>>), // expression
+ Loop(Box<AST>), // block
+ While(Box<AST>, Box<AST>), // condition, block
+ For(
+ Box<Option<AST>>,
+ Box<Option<AST>>,
+ Box<Option<AST>>,
+ Box<AST>,
+ ), // expr1, expr2, expr3, block
+ Break,
+ Continue,
+ If(Box<AST>, Box<AST>, Box<Option<AST>>), // condition, block, else/else if
+ Block(Vec<AST>), // statements
+ Decl(Name, Box<AST>), // identifier, expression
+ Expr(Box<AST>), // expr
+ // REMOVE LATER
+ Print(Box<AST>),
+
+ // expressions
+ Assign(Name, Box<AST>),
+ Ternary(Box<AST>, Box<AST>, Box<AST>),
+ Subscript(Box<AST>, Box<AST>),
+ Binary(Box<AST>, BinOp, Box<AST>),
+ Unary(UnOp, Box<AST>),
+
+ // primary
+ Lit(Lit),
+ Call(Name, Vec<AST>),
+ Member(Box<AST>, Box<AST>),
+}
+
+#[derive(Debug, Clone)]
+pub struct Name {
+ pub name: String,
+ pub start_column: usize,
+ pub end_column: usize,
+ pub line: usize,
+}
+
+#[derive(Debug, Clone)]
+pub enum Lit {
+ Ident(String),
+ Num(f32),
+ Str(String),
+ Bool(bool),
+ Array(Vec<AST>),
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum BinOp {
+ Add,
+ Sub,
+ Mul,
+ Div,
+ Mod,
+ BitAnd,
+ BitXor,
+ BitOr,
+ Eq,
+ Ne,
+ Gr,
+ Ge,
+ Ls,
+ Le,
+ Or,
+ And,
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum UnOp {
+ Not,
+ Neg,
+}
M kabel/src/codegen.rs => kabel/src/codegen.rs +25 -8
@@ 1,4 1,6 @@
-use crate::{codegen_binary, codegen_unary, opcodes::OpCode, parser::{ASTType, BinOp, Lit, UnOp, AST}, vm::{Value, VM}};
+use std::collections::HashMap;
+
+use crate::{ast::{ASTType, BinOp, Lit, Name, UnOp, AST}, codegen_binary, codegen_unary, extension::Extension, opcodes::OpCode, vm::{Value, VM}};
pub struct Codegen {
pub vm: VM
@@ 8,11 10,11 @@ impl Codegen {
pub fn new(text: String) -> Self {
Codegen {
vm: VM::new(Vec::new(), Vec::new(),
- Vec::new(), text.lines().map(|s| s.to_string()).collect()),
+ Vec::new(), HashMap::new(), text.lines().map(|s| s.to_string()).collect()),
}
}
pub fn visit(&mut self, ast: AST) {
- use crate::parser::ASTType::*;
+ use crate::ast::ASTType::*;
match ast.ast_type {
Program(asts) => {
for ast in asts {
@@ 25,6 27,9 @@ impl Codegen {
Block(stmts) => {
self.visit_block(stmts);
}
+ Decl(ref name, ref expr) => {
+ self.visit_decl(&ast, name.clone(), *expr.clone());
+ }
Expr(expr) => {
self.visit_expr_stmt(*expr);
}
@@ 85,6 90,13 @@ impl Codegen {
self.visit(stmt);
}
}
+ pub fn visit_decl(&mut self, ast: &AST, name: Name, expr: AST) {
+ self.visit(expr);
+ #[allow(irrefutable_let_patterns)]
+ if let Extension::Resolution(ptr) = ast.extensions[0] {
+ self.vm.local.insert(name.name, ptr as u8);
+ }
+ }
pub fn visit_expr_stmt(&mut self, expr: AST) {
self.visit(expr);
self.vm.chunk.push(OpCode::POP.into());
@@ 100,20 112,20 @@ impl Codegen {
}
}
pub fn visit_binary(&mut self, left: AST, oper: BinOp, right: AST) {
- use crate::parser::BinOp::*;
+ use crate::ast::BinOp::*;
codegen_binary!(self, left, right, oper, Add, ADD, Sub, SUB, Mul, MUL,
Div, DIV, Mod, MOD, BitAnd, BITAND, BitXor, BITXOR, BitOr, BITOR,
Eq, EQ, Ne, NE, Gr, GR, Ge, GE, Ls, LS, Le, LE, Or, OR, And, AND);
}
pub fn visit_unary(&mut self, oper: UnOp, right: AST) {
- use crate::parser::UnOp::*;
+ use crate::ast::UnOp::*;
codegen_unary!(self, right, oper, Not, NOT, Neg, NEG);
}
pub fn visit_lit(&mut self, ast: &AST, lit: Lit) {
match lit {
Lit::Num(value) => {
self.vm.pool.push(Value::Num(value));
- self.vm.chunk.push(OpCode::CONSTANT.into());
+ self.vm.chunk.push(OpCode::LOAD.into());
self.vm.chunk.push((self.vm.pool.len()-1) as u8);
if self.vm.lines.len() == 0 || self.vm.lines.last().unwrap().0 != ast.end_line {
self.vm.lines.push((ast.end_line, 2));
@@ 123,7 135,7 @@ impl Codegen {
}
Lit::Str(value) => {
self.vm.pool.push(Value::Str(value.into()));
- self.vm.chunk.push(OpCode::CONSTANT.into());
+ self.vm.chunk.push(OpCode::LOAD.into());
self.vm.chunk.push((self.vm.pool.len()-1) as u8);
if self.vm.lines.len() == 0 || self.vm.lines.last().unwrap().0 != ast.end_line {
self.vm.lines.push((ast.end_line, 2));
@@ 133,7 145,7 @@ impl Codegen {
}
Lit::Bool(value) => {
self.vm.pool.push(Value::Bool(value));
- self.vm.chunk.push(OpCode::CONSTANT.into());
+ self.vm.chunk.push(OpCode::LOAD.into());
self.vm.chunk.push((self.vm.pool.len()-1) as u8);
if self.vm.lines.len() == 0 || self.vm.lines.last().unwrap().0 != ast.end_line {
self.vm.lines.push((ast.end_line, 2));
@@ 141,6 153,11 @@ impl Codegen {
self.vm.lines.last_mut().unwrap().1 += 2;
}
}
+ Lit::Ident(name) => {
+ self.vm.chunk.push(OpCode::VAR.into());
+ let slot = self.vm.local.get(&name).unwrap();
+ self.vm.chunk.push(*slot);
+ }
_ => {}
}
}
M kabel/src/debug.rs => kabel/src/debug.rs +13 -6
@@ 1,4 1,4 @@
-use crate::{lexer::Token, parser::AST, push_output, vm::Value};
+use crate::{lexer::Token, ast::AST, push_output, vm::Value};
pub fn debug_token_array(tokens: Vec<Token>) -> String {
let mut output = "".to_string();
@@ 9,10 9,10 @@ pub fn debug_token_array(tokens: Vec<Token>) -> String {
output[..output.len()-1].to_string()
}
pub fn debug_ast(ast: AST, level: usize) -> String {
- use crate::parser::ASTType::*;
- use crate::parser::BinOp::*;
- use crate::parser::UnOp::*;
- use crate::parser::Lit::*;
+ use crate::ast::ASTType::*;
+ use crate::ast::BinOp::*;
+ use crate::ast::UnOp::*;
+ use crate::ast::Lit::*;
let mut output = "".to_string();
match ast.ast_type {
Program(asts) => {
@@ 207,13 207,20 @@ pub fn debug_bytecode(code: &Vec<u8>) -> String {
let mut output = "".to_string();
while ip < code.len() {
match code[ip].into() {
- CONSTANT => {
+ LOAD => {
output += &ip.to_string();
output += " CONSTANT ";
ip += 1;
output += &code[ip].to_string();
output += "\n";
}
+ VAR => {
+ output += &ip.to_string();
+ output += " VAR ";
+ ip += 1;
+ output += &code[ip].to_string();
+ output += "\n";
+ }
ADD => { output += &ip.to_string(); output += " ADD\n" }
SUB => { output += &ip.to_string(); output += " SUB\n" }
MUL => { output += &ip.to_string(); output += " MUL\n" }
A kabel/src/extension.rs => kabel/src/extension.rs +4 -0
@@ 0,0 1,4 @@
+#[derive(Debug, Clone)]
+pub enum Extension {
+ Resolution(usize), // pointer to local table
+}
M kabel/src/lib.rs => kabel/src/lib.rs +11 -8
@@ 1,10 1,11 @@
#[cfg(feature = "timer")]
use std::time::Instant;
+use ast::AST;
use codegen::Codegen;
use lexer::{Lexer, Token};
-use parser::{Parser, AST};
-use semantic_analysis::Analyzer;
+use parser::Parser;
+use name_resolution::Resolver;
pub mod debug;
pub mod error;
@@ 12,11 13,13 @@ pub mod runtime_error;
pub mod lexer;
pub mod macros;
pub mod parser;
-pub mod semantic_analysis;
+pub mod ast;
+pub mod name_resolution;
pub mod opcodes;
pub mod codegen;
pub mod vm;
pub mod test;
+pub mod extension;
pub fn run_lexer(input: String) -> Lexer {
let mut lexer = Lexer::new(input);
@@ 29,10 32,10 @@ pub fn run_parser(text: String, input: Vec<Token>) -> (AST, Parser) {
(parser.program(), parser)
}
-pub fn run_semantic_analysis(text: String, input: AST) -> Analyzer {
- let mut analyzer = Analyzer::new(text);
- analyzer.visit(input);
- analyzer
+pub fn run_semantic_analysis(text: String, input: AST) -> (AST, Resolver) {
+ let mut analyzer = Resolver::new(text);
+ let ast = analyzer.visit(input);
+ (ast, analyzer)
}
pub fn run_codegen(text: String, input: AST) -> Codegen {
@@ 90,7 93,7 @@ pub fn compile(program: String) -> String {
}
#[cfg(feature = "timer")]
let analyzer_instant = Instant::now();
- let analyzer = run_semantic_analysis(program.clone(), ast.clone());
+ let (ast, analyzer) = run_semantic_analysis(program.clone(), ast.clone());
for error in analyzer.errors.clone() {
output += &error.to_string();
output += "\n";
M kabel/src/macros.rs => kabel/src/macros.rs +8 -3
@@ 25,6 25,7 @@ macro_rules! lit {
($type:ident, $data:expr, $token:expr) => {
$crate::parser::AST {
ast_type: $crate::parser::ASTType::Lit($crate::parser::Lit::$type($data)),
+ extensions: Vec::new(),
start_line: $token.line,
end_line: $token.line,
start_column: $token.start_column,
@@ 36,8 37,9 @@ macro_rules! lit {
#[macro_export]
macro_rules! ast_from_token {
($ast_type:expr, $start:expr, $end:expr) => {
- AST {
+ $crate::parser::AST {
ast_type: $ast_type,
+ extensions: Vec::new(),
start_line: $start.line,
end_line: $end.line,
start_column: $start.start_column,
@@ 48,8 50,9 @@ macro_rules! ast_from_token {
#[macro_export]
macro_rules! ast_from_token_ast {
($ast_type:expr, $start:expr, $end:expr) => {
- AST {
+ $crate::parser::AST {
ast_type: $ast_type,
+ extensions: Vec::new(),
start_line: $start.line,
end_line: $end.end_line,
start_column: $start.start_column,
@@ 62,6 65,7 @@ macro_rules! ast_from_ast {
($ast_type:expr, $start:expr, $end:expr) => {
AST {
ast_type: $ast_type,
+ extensions: Vec::new(),
start_line: $start.start_line,
end_line: $end.end_line,
start_column: $start.start_column,
@@ 74,6 78,7 @@ macro_rules! ast_from_ast_token {
($ast_type:expr, $start:expr, $end:expr) => {
AST {
ast_type: $ast_type,
+ extensions: Vec::new(),
start_line: $start.start_line,
end_line: $end.line,
start_column: $start.start_column,
@@ 85,7 90,7 @@ macro_rules! ast_from_ast_token {
#[macro_export]
macro_rules! name {
($name:expr, $token:expr) => {
- Name {
+ $crate::ast::Name {
name: $name,
start_column: $token.start_column,
end_column: $token.end_column,
M kabel/src/main.rs => kabel/src/main.rs +10 -2
@@ 10,7 10,14 @@ fn main() {
fs::read_to_string(args[1].clone()).unwrap();*/
let program =
-"2+2;
+"var a = 2;
+var b = 3;
+print a+b;
+{
+ var b = 7;
+ var c = 4;
+ print c;
+}
".to_string();
let mut output = "".to_string();
@@ 41,7 48,7 @@ fn main() {
output += &debug_ast(ast.clone(), 0);
output += "\n\n";
//output += &format!("{:#?}", ast);
- let analyzer = run_semantic_analysis(program.to_string(), ast.clone());
+ let (ast, analyzer) = run_semantic_analysis(program.to_string(), ast.clone());
for error in analyzer.errors.clone() {
output += &error.to_string();
output += "\n";
@@ 50,6 57,7 @@ fn main() {
println!("{}", output);
return;
}
+ //output += &format!("{:#?}", ast);
let codegen = run_codegen(program, ast);
A kabel/src/name_resolution.rs => kabel/src/name_resolution.rs +230 -0
@@ 0,0 1,230 @@
+use std::collections::HashMap;
+
+use crate::{ast::{ASTType, AST}, ast_from_ast, collect_lines, error::{ErrorKind, KabelError}, extension::Extension, out_of_scope, out_of_scope_var};
+
+pub struct Resolver {
+ text: Vec<String>,
+ symbol_table: Vec<HashMap<String, (Symbol, usize)>>, // (Symbol, reference to locals)
+ pub locals: Vec<usize>, // reference to stack
+ pub errors: Vec<KabelError>,
+}
+
+impl Resolver {
+ pub fn new(text: String) -> Self {
+ Self {
+ text: text.lines().collect::<Vec<&str>>().iter().map(|s| s.to_string()).collect(),
+ symbol_table: vec![HashMap::new()],
+ locals: Vec::new(),
+ errors: Vec::new(),
+ }
+ }
+ pub fn visit(&mut self, ast: AST) -> AST {
+ use ASTType::*;
+ match ast.ast_type {
+ Program(asts) => {
+ let mut program = Vec::new();
+ for ast in asts {
+ let ast = self.visit(ast.clone());
+ program.push(ast)
+ }
+ AST {
+ ast_type: ASTType::Program(program),
+ extensions: Vec::new(),
+ start_line: 0,
+ end_line: 0,
+ start_column: 0,
+ end_column: 0,
+ }
+ }
+ Function(name, args, block) => {
+ self.symbol_table.last_mut().unwrap().insert(name.name.clone(), (Symbol::Function(args.len()),0));
+ self.symbol_table.push(HashMap::new());
+ for arg in args.clone() {
+ self.symbol_table.last_mut().unwrap().insert(arg.name, (Symbol::Var,0));
+ }
+ let block = self.visit(*block);
+ self.symbol_table.pop();
+ ast_from_ast!(Function(name, args, Box::new(block)), ast, ast)
+ }
+ Return(expr) => {
+ if let Some(expr) = *expr {
+ let expr = self.visit(expr);
+ return ast_from_ast!(Return(Box::new(Some(expr))), ast, ast);
+ }
+ ast_from_ast!(Return(Box::new(None)), ast, ast)
+ }
+ Loop(block) => {
+ let block = self.visit(*block);
+ ast_from_ast!(Loop(Box::new(block)), ast, ast)
+ }
+ While(condition, block) => {
+ let condition = self.visit(*condition);
+ let block = self.visit(*block);
+ ast_from_ast!(While(Box::new(condition), Box::new(block)), ast, ast)
+ }
+ For(expr1, expr2, expr3, block) => {
+ let mut n_expr1 = None;
+ let mut n_expr2 = None;
+ let mut n_expr3 = None;
+ if let Some(expr) = *expr1 {
+ n_expr1 = Some(self.visit(expr));
+ }
+ if let Some(expr) = *expr2 {
+ n_expr2 = Some(self.visit(expr));
+ }
+ if let Some(expr) = *expr3 {
+ n_expr3 = Some(self.visit(expr));
+ }
+ let block = self.visit(*block);
+ ast_from_ast!(For(Box::new(n_expr1), Box::new(n_expr2), Box::new(n_expr3), Box::new(block)), ast, ast)
+ }
+ If(condition, block, else_expr) => {
+ let condition = self.visit(*condition);
+ let block = self.visit(*block);
+ let mut n_else_expr = None;
+ if let Some(else_expr) = *else_expr {
+ n_else_expr = Some(self.visit(else_expr));
+ }
+ ast_from_ast!(If(Box::new(condition), Box::new(block), Box::new(n_else_expr)), ast, ast)
+ }
+ Block(stmts) => {
+ self.symbol_table.push(HashMap::new());
+ let mut n_stmts = Vec::new();
+ for stmt in stmts {
+ n_stmts.push(self.visit(stmt));
+ }
+ self.symbol_table.pop();
+ ast_from_ast!(Block(n_stmts), ast, ast)
+ }
+ Decl(name, expr) => {
+ let expr = self.visit(*expr);
+ self.locals.push(0);
+ self.symbol_table.last_mut().unwrap().insert(name.name.clone(), (Symbol::Var, self.locals.len()-1));
+ AST {
+ ast_type: Decl(name, Box::new(expr)),
+ extensions: vec![Extension::Resolution(self.locals.len()-1)],
+ start_line: ast.start_line,
+ end_line: ast.end_line,
+ start_column: ast.start_column,
+ end_column: ast.end_column,
+ }
+ }
+ Expr(expr) => {
+ let expr = self.visit(*expr);
+ ast_from_ast!(Expr(Box::new(expr)), ast, ast)
+ }
+ // REMOVE LATER
+ Print(expr) => {
+ let expr = self.visit(*expr);
+ ast_from_ast!(Print(Box::new(expr)), ast, ast)
+ }
+ Assign(name, expr) => {
+ let expr = self.visit(*expr);
+ if !self.symbol_table.last().unwrap().contains_key(&name.name) {
+ self.errors.push(out_of_scope_var!(self, "Variable \"{}\" not in scope", name, ast));
+ }
+ ast_from_ast!(Assign(name, Box::new(expr)), ast, ast)
+ }
+ Ternary(condition, true_expr, false_expr) => {
+ let condition = self.visit(*condition);
+ let true_expr = self.visit(*true_expr);
+ let false_expr = self.visit(*false_expr);
+ ast_from_ast!(Ternary(Box::new(condition), Box::new(true_expr), Box::new(false_expr)), ast, ast)
+ }
+ Subscript(array, index) => {
+ let array = self.visit(*array);
+ let index = self.visit(*index);
+ ast_from_ast!(Subscript(Box::new(array), Box::new(index)), ast, ast)
+ }
+ Binary(left, oper, right) => {
+ let left = self.visit(*left);
+ let right = self.visit(*right);
+ ast_from_ast!(Binary(Box::new(left), oper, Box::new(right)), ast, ast)
+ }
+ Unary(oper, right) => {
+ let right = self.visit(*right);
+ ast_from_ast!(Unary(oper, Box::new(right)), ast, ast)
+ }
+ Lit(ref lit) => {
+ let lit = lit.clone();
+ match lit {
+ crate::ast::Lit::Ident(ref name) => {
+ let resolution = self.resolve_var(name);
+ if !resolution.0 {
+ self.errors.push(out_of_scope!(self, "Variable \"{}\" not in scope", name, ast))
+ } else {
+ return AST {
+ ast_type: Lit(lit),
+ extensions: vec![Extension::Resolution(resolution.1)],
+ start_line: ast.start_line,
+ end_line: ast.end_line,
+ start_column: ast.start_column,
+ end_column: ast.end_column,
+ };
+ }
+ }
+ _ => {}
+ }
+ ast_from_ast!(Lit(lit), ast, ast)
+ }
+ Call(ident, args) => {
+ if let Err(e) = self.resolve_function(&ident.name, args.len()) {
+ match e {
+ (ErrorKind::OutOfScope, _, _) => self.errors.push(out_of_scope!(self, "Function \"{}\" not in scope", ident.name, ast)),
+ (ErrorKind::IncorrectArity, Some(f_arity), Some(arity)) => {
+ self.errors.push(
+ KabelError::new(
+ ErrorKind::IncorrectArity,
+ format!("Function {} has {} argument, provided {}", ident.name, f_arity, arity),
+ ast.start_line,
+ ast.start_column,
+ collect_lines!(self.text[ast.start_line-1..ast.end_line-1]),
+ )
+ );
+ }
+ _ => { panic!("Returned invalid ErrorKind from resolve_function") },
+ }
+ }
+ let mut n_args = Vec::new();
+ for arg in args {
+ n_args.push(self.visit(arg));
+ }
+ ast_from_ast!(Call(ident, n_args), ast, ast)
+ }
+ /*Member(left, right) => {
+ self.visit_member(*left, *right);
+ }*/
+ _ => { panic!("not implemented") } // not implemented
+ }
+ }
+ // TODO: make visit_member not throw out of scope errors
+ /*pub fn visit_member(&mut self, left: AST, right: AST) {
+ self.visit(left);
+ self.visit(right);
+ }*/
+ 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) {
+ return (true, *place);
+ }
+ }
+ (false, 0)
+ }
+ fn resolve_function(&mut self, name: &String, arity: usize) -> Result<(), (ErrorKind, Option<usize>, Option<usize>)>{
+ for scope in self.symbol_table.iter().rev() {
+ if let Some((Symbol::Function(f_arity), _place)) = scope.get(name) {
+ if *f_arity == arity {
+ return Ok(());
+ } else {
+ return Err((ErrorKind::IncorrectArity, Some(*f_arity), Some(arity)));
+ }
+ }
+ }
+ Err((ErrorKind::OutOfScope, None, None))
+ }
+}
+
+pub enum Symbol {
+ Var,
+ Function(usize),
+}
M kabel/src/opcodes.rs => kabel/src/opcodes.rs +49 -46
@@ 1,7 1,8 @@
#![allow(non_camel_case_types)]
pub enum OpCode {
- CONSTANT,
+ LOAD,
+ VAR,
ADD,
SUB,
@@ 35,34 36,35 @@ impl From<OpCode> for u8 {
fn from(value: OpCode) -> Self {
use OpCode::*;
match value {
- CONSTANT => 0x00,
+ LOAD => 0x00,
+ VAR => 0x01,
- ADD => 0x01,
- SUB => 0x02,
- MUL => 0x03,
- DIV => 0x04,
- MOD => 0x05,
- BITAND => 0x06,
- BITXOR => 0x07,
- BITOR => 0x08,
- EQ => 0x09,
- NE => 0x0A,
- GR => 0x0B,
- GE => 0x0C,
- LS => 0x0D,
- LE => 0x0E,
- OR => 0x0F,
- AND => 0x10,
+ ADD => 0x02,
+ SUB => 0x03,
+ MUL => 0x04,
+ DIV => 0x05,
+ MOD => 0x06,
+ BITAND => 0x07,
+ BITXOR => 0x08,
+ BITOR => 0x09,
+ EQ => 0x010,
+ NE => 0x0A,
+ GR => 0x0B,
+ GE => 0x0C,
+ LS => 0x0D,
+ LE => 0x0E,
+ OR => 0x0F,
+ AND => 0x11,
- NOT => 0x11,
- NEG => 0x12,
+ NOT => 0x12,
+ NEG => 0x13,
- JMP => 0x13,
- IF_NE => 0x14,
+ JMP => 0x14,
+ IF_NE => 0x15,
- POP => 0xFD,
- PRINT => 0xFE,
- ERR => 0xFF
+ POP => 0xFD,
+ PRINT => 0xFE,
+ ERR => 0xFF
}
}
}
@@ 70,30 72,31 @@ impl From<u8> for OpCode {
fn from(value: u8) -> Self {
use OpCode::*;
match value {
- 0x00 => CONSTANT,
+ 0x00 => LOAD,
+ 0x01 => VAR,
- 0x01 => ADD,
- 0x02 => SUB,
- 0x03 => MUL,
- 0x04 => DIV,
- 0x05 => MOD,
- 0x06 => BITAND,
- 0x07 => BITXOR,
- 0x08 => BITOR,
- 0x09 => EQ,
- 0x0A => NE,
- 0x0B => GR,
- 0x0C => GE,
- 0x0D => LS,
- 0x0E => LE,
- 0x0F => OR,
- 0x10 => AND,
+ 0x02 => ADD,
+ 0x03 => SUB,
+ 0x04 => MUL,
+ 0x05 => DIV,
+ 0x06 => MOD,
+ 0x07 => BITAND,
+ 0x08 => BITXOR,
+ 0x09 => BITOR,
+ 0x0A => EQ,
+ 0x0B => NE,
+ 0x0C => GR,
+ 0x0D => GE,
+ 0x0E => LS,
+ 0x0F => LE,
+ 0x10 => OR,
+ 0x11 => AND,
- 0x11 => NOT,
- 0x12 => NEG,
+ 0x12 => NOT,
+ 0x13 => NEG,
- 0x13 => JMP,
- 0x14 => IF_NE,
+ 0x14 => JMP,
+ 0x15 => IF_NE,
0xFD => POP,
0xFE => PRINT,
M kabel/src/parser.rs => kabel/src/parser.rs +33 -117
@@ 1,6 1,7 @@
use crate::{
- ast_from_ast, ast_from_ast_token, ast_from_token, ast_from_token_ast, collect_lines, error::{ErrorKind, KabelError}, lexer::{Token, TokenType}, lit, name, unexpected_token
+ ast_from_ast, ast_from_ast_token, ast_from_token, ast_from_token_ast, collect_lines, error::{ErrorKind, KabelError}, extension::Extension, lexer::{Token, TokenType}, lit, name, unexpected_token
};
+use crate::ast::{AST, ASTType, BinOp, UnOp, Lit};
pub struct Parser {
input: Vec<Token>,
@@ 34,6 35,7 @@ impl Parser {
}
AST {
ast_type: ASTType::Program(program),
+ extensions: Vec::new(),
start_line: 0,
end_line: 0,
start_column: 0,
@@ 52,6 54,7 @@ impl Parser {
TokenType::Continue => self.continue_statement(),
TokenType::If => self.if_statement(),
TokenType::LeftBrace => self.block(),
+ TokenType::Var => self.declaration(),
TokenType::Print => self.print_statement(), // REMOVE LATER
_ => self.expression_statement(),
}
@@ 296,6 299,35 @@ impl Parser {
}
}
+ pub fn declaration(&mut self) -> Result<AST, KabelError> {
+ let var = self.read_token()?;
+ let ident = self.read_token()?;
+ if let TokenType::Ident(name) = ident.token_type {
+ let equal = self.read_token()?;
+ if let TokenType::Equal = equal.token_type {
+ let expr = self.expression()?;
+ let semicolon = self.read_token()?;
+ if let TokenType::Semicolon = semicolon.token_type {
+ return Ok(ast_from_token!(
+ ASTType::Decl(name!(name, ident), Box::new(expr.clone())),
+ var,
+ semicolon
+ ));
+ } else {
+ return Err(unexpected_token!(self, "Expected ; found {}", equal));
+ }
+ } else {
+ return Err(unexpected_token!(self, "Expected = found {}", equal));
+ }
+ } else {
+ return Err(unexpected_token!(
+ self,
+ "Expected identifier found {}",
+ ident
+ ));
+ }
+ }
+
// REMOVE LATER
pub fn print_statement(&mut self) -> Result<AST, KabelError> {
let print_ident = self.read_token()?;
@@ 331,37 363,10 @@ impl Parser {
}
pub fn expression(&mut self) -> Result<AST, KabelError> {
- if let TokenType::Var = self.peek()?.token_type {
- return self.declaration();
- }
let assignment = self.assignment()?;
Ok(assignment)
}
- pub fn declaration(&mut self) -> Result<AST, KabelError> {
- let var = self.read_token()?;
- let ident = self.read_token()?;
- if let TokenType::Ident(name) = ident.token_type {
- let equal = self.read_token()?;
- if let TokenType::Equal = equal.token_type {
- let expr = self.expression()?;
- return Ok(ast_from_token_ast!(
- ASTType::Decl(name!(name, ident), Box::new(expr.clone())),
- var,
- expr
- ));
- } else {
- return Err(unexpected_token!(self, "Expected = found {}", equal));
- }
- } else {
- return Err(unexpected_token!(
- self,
- "Expected identifier found {}",
- ident
- ));
- }
- }
-
pub fn assignment(&mut self) -> Result<AST, KabelError> {
if let TokenType::Ident(name) = self.peek()?.token_type {
let ident = self.read_token()?;
@@ 1057,92 1062,3 @@ impl Parser {
return Ok(self.input[self.current].clone());
}
}
-
-#[derive(Debug, Clone)]
-pub struct AST {
- pub ast_type: ASTType,
- pub start_line: usize,
- pub end_line: usize,
- pub start_column: usize,
- pub end_column: usize,
-}
-
-#[derive(Debug, Clone)]
-pub enum ASTType {
- Program(Vec<AST>),
-
- // statements
- Function(Name, Vec<Name>, Box<AST>), // name, args, block
- Return(Box<Option<AST>>), // expression
- Loop(Box<AST>), // block
- While(Box<AST>, Box<AST>), // condition, block
- For(
- Box<Option<AST>>,
- Box<Option<AST>>,
- Box<Option<AST>>,
- Box<AST>,
- ), // expr1, expr2, expr3, block
- Break,
- Continue,
- If(Box<AST>, Box<AST>, Box<Option<AST>>), // condition, block, else/else if
- Block(Vec<AST>), // statements
- Expr(Box<AST>), // expr
- // REMOVE LATER
- Print(Box<AST>),
-
- // expressions
- Decl(Name, Box<AST>), // identifier, expression
- Assign(Name, Box<AST>),
- Ternary(Box<AST>, Box<AST>, Box<AST>),
- Subscript(Box<AST>, Box<AST>),
- Binary(Box<AST>, BinOp, Box<AST>),
- Unary(UnOp, Box<AST>),
-
- // primary
- Lit(Lit),
- Call(Name, Vec<AST>),
- Member(Box<AST>, Box<AST>),
-}
-
-#[derive(Debug, Clone)]
-pub struct Name {
- pub name: String,
- pub start_column: usize,
- pub end_column: usize,
- pub line: usize,
-}
-
-#[derive(Debug, Clone)]
-pub enum Lit {
- Ident(String),
- Num(f32),
- Str(String),
- Bool(bool),
- Array(Vec<AST>),
-}
-
-#[derive(Debug, Clone, Copy)]
-pub enum BinOp {
- Add,
- Sub,
- Mul,
- Div,
- Mod,
- BitAnd,
- BitXor,
- BitOr,
- Eq,
- Ne,
- Gr,
- Ge,
- Ls,
- Le,
- Or,
- And,
-}
-
-#[derive(Debug, Clone, Copy)]
-pub enum UnOp {
- Not,
- Neg,
-}
D kabel/src/semantic_analysis.rs => kabel/src/semantic_analysis.rs +0 -222
@@ 1,222 0,0 @@
-use std::collections::HashMap;
-
-use crate::{collect_lines, error::{ErrorKind, KabelError}, out_of_scope, out_of_scope_var, parser::{ASTType, Lit, Name, AST}};
-
-pub struct Analyzer {
- text: Vec<String>,
- symbol_table: Vec<HashMap<String, Symbol>>,
- pub errors: Vec<KabelError>,
-}
-
-impl Analyzer {
- pub fn new(text: String) -> Self {
- Self {
- text: text.lines().collect::<Vec<&str>>().iter().map(|s| s.to_string()).collect(),
- symbol_table: vec![HashMap::new()],
- errors: Vec::new(),
- }
- }
- pub fn visit(&mut self, ast: AST) {
- use ASTType::*;
- match ast.ast_type {
- Program(asts) => {
- self.visit_program(asts);
- }
- Function(name, args, block) => {
- self.visit_function(name, args, *block);
- }
- Return(expr) => {
- self.visit_return(*expr);
- }
- Loop(block) => {
- self.visit_loop(*block);
- }
- While(condition, block) => {
- self.visit_while(*condition, *block);
- }
- For(expr1, expr2, expr3, block) => {
- self.visit_for(*expr1, *expr2, *expr3, *block);
- }
- If(condition, block, else_expr) => {
- self.visit_if(*condition, *block, *else_expr);
- }
- Block(stmts) => {
- self.visit_block(stmts);
- }
- Expr(expr) => {
- self.visit(*expr);
- }
- // REMOVE LATER
- Print(expr) => {
- self.visit_print(*expr);
- }
- Decl(name, expr) => {
- self.visit_decl(name, *expr);
- }
- Assign(ref name, ref expr) => {
- self.visit_assign(ast.clone(), name.clone(), *expr.clone());
- }
- Ternary(condition, true_expr, false_expr) => {
- self.visit_ternary(*condition, *true_expr, *false_expr);
- }
- Subscript(array, index) => {
- self.visit_subscript(*array, *index);
- }
- Binary(left, _oper, right) => {
- self.visit_binary(*left, *right);
- }
- Unary(_oper, right) => {
- self.visit_unary(*right);
- }
- Lit(ref lit) => {
- self.visit_lit(ast.clone(), lit.clone());
- }
- Call(ref ident, ref args) => {
- self.visit_call(ast.clone(), ident.clone(), args.clone());
- }
- /*Member(left, right) => {
- self.visit_member(*left, *right);
- }*/
- _ => {} // not implemented
- }
- }
- pub fn visit_program(&mut self, asts: Vec<AST>) {
- for ast in asts {
- self.visit(ast);
- }
- }
- pub fn visit_function(&mut self, name: Name, args: Vec<Name>, block: AST) {
- self.symbol_table.last_mut().unwrap().insert(name.name.clone(), Symbol::Function(args.len()));
- self.symbol_table.push(HashMap::new());
- for arg in args {
- self.symbol_table.last_mut().unwrap().insert(arg.name, Symbol::Var);
- }
- self.visit(block);
- self.symbol_table.pop();
- }
- pub fn visit_return(&mut self, expr: Option<AST>) {
- if let Some(expr) = expr {
- self.visit(expr);
- }
- }
- pub fn visit_loop(&mut self, block: AST) {
- self.visit(block);
- }
- pub fn visit_while(&mut self, condition: AST, block: AST) {
- self.visit(condition);
- self.visit(block);
- }
- pub fn visit_for(&mut self, expr1: Option<AST>, expr2: Option<AST>, expr3: Option<AST>, block: AST) {
- if let Some(expr) = expr1 {
- self.visit(expr);
- }
- if let Some(expr) = expr2 {
- self.visit(expr);
- }
- if let Some(expr) = expr3 {
- self.visit(expr);
- }
- self.visit(block);
- }
- pub fn visit_if(&mut self, condition: AST, block: AST, else_expr: Option<AST>) {
- self.visit(condition);
- self.visit(block);
- if let Some(else_expr) = else_expr {
- self.visit(else_expr);
- }
- }
- pub fn visit_block(&mut self, stmts: Vec<AST>) {
- self.symbol_table.push(HashMap::new());
- for stmt in stmts {
- self.visit(stmt);
- }
- 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);
- }
- pub fn visit_assign(&mut self, ast: AST, name: Name, expr: AST) {
- self.visit(expr.clone());
- if !self.symbol_table.last().unwrap().contains_key(&name.name) {
- self.errors.push(out_of_scope_var!(self, "Variable \"{}\" not in scope", name, ast))
- }
- }
- pub fn visit_ternary(&mut self, condition: AST, true_expr: AST, false_expr: AST) {
- self.visit(condition);
- self.visit(true_expr);
- self.visit(false_expr);
- }
- pub fn visit_subscript(&mut self, array: AST, index: AST) {
- self.visit(array);
- self.visit(index);
- }
- pub fn visit_binary(&mut self, left: AST, right: AST) {
- self.visit(left);
- self.visit(right);
- }
- pub fn visit_unary(&mut self, right: AST) {
- self.visit(right);
- }
- pub fn visit_lit(&mut self, ast: AST, lit: Lit) {
- match lit {
- Lit::Ident(name) => {
- if !self.resolve_var(&name) {
- self.errors.push(out_of_scope!(self, "Variable \"{}\" not in scope", name, ast))
- }
- }
- _ => {}
- }
- }
- pub fn visit_call(&mut self, ast: AST, ident: Name, args: Vec<AST>) {
- if !self.resolve_function(&ast, &ident.name, args.len()) {
- self.errors.push(out_of_scope!(self, "Function \"{}\" not in scope", ident.name, ast))
- }
- for arg in args {
- self.visit(arg);
- }
- }
- // TODO: make visit_member not throw out of scope errors
- /*pub fn visit_member(&mut self, left: AST, right: AST) {
- self.visit(left);
- self.visit(right);
- }*/
- fn resolve_var(&self, name: &String) -> bool {
- for scope in self.symbol_table.iter().rev() {
- if matches!(scope.get(name), Some(Symbol::Var)) {
- return true;
- }
- }
- false
- }
- fn resolve_function(&mut self, ast: &AST, name: &String, arity: usize) -> bool {
- for scope in self.symbol_table.iter().rev() {
- if let Some(Symbol::Function(f_arity)) = scope.get(name) {
- if *f_arity == arity {
- return true;
- } else {
- self.errors.push(
- KabelError::new(
- ErrorKind::OutOfScope,
- format!("Function {} has {} argument, provided {}", name, *f_arity, arity),
- ast.start_line,
- ast.start_column,
- collect_lines!(self.text[ast.start_line-1..ast.end_line-1]),
- )
- );
- return true;
- }
- }
- }
- false
- }
-}
-
-pub enum Symbol {
- Var,
- Function(usize),
-}
M kabel/src/vm.rs => kabel/src/vm.rs +31 -22
@@ 1,3 1,5 @@
+use std::collections::HashMap;
+
use crate::{mismatched_types, runtime_error::KabelRuntimeError, vm_boolean_binary, wrong_type};
pub struct VM {
@@ 5,17 7,20 @@ pub struct VM {
pub chunk: Vec<u8>,
pub stack: Vec<Value>,
pub pool: Vec<Value>,
+ pub local: HashMap<String, u8>, // (name, stack pointer)
pub 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 {
+ pub fn new(bytecode: Vec<u8>, lines: Vec<(usize, usize)>, pool: Vec<Value>,
+ local: HashMap<String, u8>, text: String) -> Self {
Self {
ip: 0,
chunk: bytecode,
stack: Vec::new(),
pool,
+ local,
lines,
text: text.lines().map(|s| s.to_string()).collect(),
}
@@ 24,11 29,15 @@ impl VM {
use Value::*;
while self.ip < self.chunk.len() {
match self.read() {
- 0x00 => { // CONSTANT
+ 0x00 => { // LOAD
let byte = self.read() as usize;
self.stack.push(self.pool[byte].clone());
}
- 0x01 => { // ADD
+ 0x01 => { // VAR
+ let ptr = self.read() as usize;
+ self.stack.push(self.stack[ptr].clone());
+ }
+ 0x02 => { // ADD
match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
(Num(v1), Num(v2)) => self.stack.push(Num(v1 + v2)),
(Str(v1), Str(v2)) => {
@@ 42,7 51,7 @@ impl VM {
}
}
}
- 0x02 => { // SUB
+ 0x03 => { // SUB
match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
(Num(v1), Num(v2)) => self.stack.push(Num(v1 - v2)),
(Str(_v1), Str(_v2)) => {
@@ 56,7 65,7 @@ impl VM {
}
}
}
- 0x03 => { // MUL
+ 0x04 => { // MUL
match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
(Num(v1), Num(v2)) => self.stack.push(Num(v1 * v2)),
(Str(v1), Num(v2)) => {
@@ 77,7 86,7 @@ impl VM {
}
}
}
- 0x04 => { // DIV
+ 0x05 => { // DIV
match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
(Num(v1), Num(v2)) => self.stack.push(Num(v1 / v2)),
(Str(_v1), Str(_v2)) => {
@@ 91,7 100,7 @@ impl VM {
}
}
}
- 0x05 => { // MOD
+ 0x06 => { // MOD
match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
(Num(v1), Num(v2)) => self.stack.push(Num(v1 % v2)),
(Str(_v1), Str(_v2)) => {
@@ 105,7 114,7 @@ impl VM {
}
}
}
- 0x06 => { // BITAND
+ 0x07 => { // BITAND
match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
(Num(v1), Num(v2)) => {
if v1.fract() != 0.0 {
@@ 127,7 136,7 @@ impl VM {
}
}
}
- 0x07 => { // BITXOR
+ 0x08 => { // BITXOR
match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
(Num(v1), Num(v2)) => {
if v1.fract() != 0.0 {
@@ 149,7 158,7 @@ impl VM {
}
}
}
- 0x08 => { // BITOR
+ 0x09 => { // BITOR
match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
(Num(v1), Num(v2)) => {
if v1.fract() != 0.0 {
@@ 172,19 181,19 @@ impl VM {
}
}
// EQ
- 0x09 => vm_boolean_binary!(self, ==),
+ 0x0a => vm_boolean_binary!(self, ==),
// NE
- 0x0A => vm_boolean_binary!(self, !=),
+ 0x0B => vm_boolean_binary!(self, !=),
// GR
- 0x0B => vm_boolean_binary!(self, >),
+ 0x0C => vm_boolean_binary!(self, >),
// GE
- 0x0C => vm_boolean_binary!(self, >=),
+ 0x0D => vm_boolean_binary!(self, >=),
// LS
- 0x0D => vm_boolean_binary!(self, <),
+ 0x0E => vm_boolean_binary!(self, <),
// LE
- 0x0E => vm_boolean_binary!(self, <=),
+ 0x0F => vm_boolean_binary!(self, <=),
// OR
- 0x0F => match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
+ 0x10 => match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
(Num(_v1), Num(_v2)) => {
return Err(wrong_type!(self, "Cannot perform logical OR on numbers"))
}
@@ 197,7 206,7 @@ impl VM {
}
}
// AND
- 0x10 => match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
+ 0x11 => match (self.stack.pop().unwrap(), self.stack.pop().unwrap()) {
(Num(_v1), Num(_v2)) => {
return Err(wrong_type!(self, "Cannot perform logical AND on numbers"))
}
@@ 210,7 219,7 @@ impl VM {
}
}
// NOT
- 0x11 => match self.stack.pop().unwrap() {
+ 0x12 => match self.stack.pop().unwrap() {
Num(_v1) => {
return Err(wrong_type!(self, "Cannot perform logical NOT on numbers"))
}
@@ 220,7 229,7 @@ impl VM {
Bool(v1) => self.stack.push(Bool(!v1)),
}
// NEG
- 0x12 => match self.stack.pop().unwrap() {
+ 0x13 => match self.stack.pop().unwrap() {
Num(v1) => self.stack.push(Num(-v1)),
Str(_v1) => {
return Err(wrong_type!(self, "Cannot negate strings"))
@@ 230,12 239,12 @@ impl VM {
}
}
// JMP
- 0x13 => {
+ 0x14 => {
let loc = self.read_u16();
self.ip += loc as usize;
}
// IF_NE
- 0x14 => {
+ 0x15 => {
let condition = self.stack.pop().unwrap();
if let Value::Bool(condition) = condition {
if !condition {
M kabel/test/syntax/assignment.out => kabel/test/syntax/assignment.out +4 -6
@@ 1,10 1,8 @@
Program
-| Expr
-| | Decl i
-| | | Lit 0
+| Decl i
+| | Lit 0
| Expr
| | Assign i
| | | Lit string
-| Expr
-| | Decl _foo
-| | | Lit 3
+| Decl _foo
+| | Lit 3