M kabel/opcodes.txt => kabel/opcodes.txt +18 -0
@@ 4,4 4,22 @@ 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
+
+NOT ; 0x11
+NEG ; 0x12
+
+JMP LOC ; 0x13
+IF_NE ELSE ; 0x14
+
PRINT ; 0xFF
M kabel/src/codegen.rs => kabel/src/codegen.rs +54 -1
@@ 1,4 1,4 @@
-use crate::{codegen_binary, codegen_unary, opcodes::OpCode, parser::{BinOp, Lit, UnOp, AST}, vm::{Value, VM}};
+use crate::{codegen_binary, codegen_unary, opcodes::OpCode, parser::{ASTType, BinOp, Lit, UnOp, AST}, vm::{Value, VM}};
pub struct Codegen {
pub vm: VM
@@ 19,6 19,12 @@ impl Codegen {
self.visit(ast);
}
}
+ If(condition, block, else_expr) => {
+ self.visit_if(*condition, *block, *else_expr);
+ }
+ Block(stmts) => {
+ self.visit_block(stmts);
+ }
// REMOVE LATER
Print(ref expr) => {
self.visit_print(&ast, *expr.clone());
@@ 35,6 41,47 @@ impl Codegen {
_ => {}
}
}
+ pub fn visit_if(&mut self, condition: AST, block: AST, else_expr: Option<AST>) {
+ self.visit(condition);
+ self.vm.chunk.push(OpCode::IF_NE.into());
+ self.vm.chunk.push(0xFF); // placeholder
+ self.vm.chunk.push(0xFF); // placeholder
+ let start_jmp_loc = self.vm.chunk.len()-2;
+ self.visit(block);
+ if let Some(ast) = else_expr {
+ match ast.ast_type {
+ ASTType::If(_, _, _) => {
+ self.vm.chunk.push(OpCode::JMP.into());
+ self.vm.chunk.push(0xFF); // placeholder
+ self.vm.chunk.push(0xFF); // placeholder
+ let end_jmp_loc = self.vm.chunk.len()-2;
+
+ self.patch_jump(start_jmp_loc);
+ self.visit(ast);
+ self.patch_jump(end_jmp_loc);
+ }
+ ASTType::Block(_) => {
+
+ self.vm.chunk.push(OpCode::JMP.into());
+ self.vm.chunk.push(0xFF); // placeholder
+ self.vm.chunk.push(0xFF); // placeholder
+
+ let end_jmp_loc = self.vm.chunk.len()-2;
+ self.patch_jump(start_jmp_loc); // jmp to else
+ self.visit(ast);
+ self.patch_jump(end_jmp_loc); // jmp to after else
+ }
+ _ => {}
+ }
+ } else {
+ self.patch_jump(start_jmp_loc);
+ }
+ }
+ pub fn visit_block(&mut self, stmts: Vec<AST>) {
+ for stmt in stmts {
+ self.visit(stmt);
+ }
+ }
// REMOVE LATER
pub fn visit_print(&mut self, ast: &AST, expr: AST) {
self.visit(expr);
@@ 90,4 137,10 @@ impl Codegen {
_ => {}
}
}
+ pub fn patch_jump(&mut self, loc: usize) {
+ let jump = self.vm.chunk.len() - loc - 2;
+
+ self.vm.chunk[loc] = ((jump >> 8) & 0xFF) as u8;
+ self.vm.chunk[loc + 1] = (jump & 0xFF) as u8;
+ }
}
M kabel/src/debug.rs => kabel/src/debug.rs +59 -0
@@ 195,3 195,62 @@ pub fn debug_ast(ast: AST, level: usize) -> String {
}
output
}
+
+pub fn debug_bytecode(code: Vec<u8>) -> String {
+ use crate::opcodes::OpCode::*;
+ let mut ip = 0;
+ let mut output = "".to_string();
+ while ip < code.len() {
+ match code[ip].into() {
+ CONSTANT => {
+ output += &ip.to_string();
+ output += " CONSTANT ";
+ 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" }
+ DIV => { output += &ip.to_string(); output += " DIV\n" }
+ MOD => { output += &ip.to_string(); output += " MOD\n" }
+ BITAND => { output += &ip.to_string(); output += " BITAND\n" }
+ BITXOR => { output += &ip.to_string(); output += " BITXOR\n" }
+ BITOR => { output += &ip.to_string(); output += " BITOR\n" }
+ EQ => { output += &ip.to_string(); output += " EQ\n" }
+ NE => { output += &ip.to_string(); output += " NE\n" }
+ GR => { output += &ip.to_string(); output += " GR\n" }
+ GE => { output += &ip.to_string(); output += " GE\n" }
+ LS => { output += &ip.to_string(); output += " LS\n" }
+ LE => { output += &ip.to_string(); output += " LE\n" }
+ OR => { output += &ip.to_string(); output += " OR\n" }
+ AND => { output += &ip.to_string(); output += " AND\n" }
+ NOT => { output += &ip.to_string(); output += " NOT\n" }
+ NEG => { output += &ip.to_string(); output += " NEG\n" }
+ JMP => {
+ output += &ip.to_string();
+ output += " JMP ";
+ ip += 1;
+ let byte_one = (code[ip] as u16) << 8;
+ ip += 1;
+ let byte_two = code[ip] as u16;
+ output += &(byte_one | byte_two).to_string();
+ output += "\n";
+ }
+ IF_NE => {
+ output += &ip.to_string();
+ output += " IF_NE ";
+ ip += 1;
+ let byte_one = (code[ip] as u16) << 8;
+ ip += 1;
+ let byte_two = code[ip] as u16;
+ output += &(byte_one | byte_two).to_string();
+ output += "\n";
+ }
+ PRINT => { output += &ip.to_string(); output += " PRINT\n" }
+ ERR => output += "ERR\n",
+ }
+ ip += 1;
+ }
+ output
+}
M kabel/src/opcodes.rs => kabel/src/opcodes.rs +15 -1
@@ 1,3 1,5 @@
+#![allow(non_camel_case_types)]
+
pub enum OpCode {
CONSTANT,
@@ 20,6 22,10 @@ pub enum OpCode {
NOT,
NEG,
+
+ JMP,
+ IF_NE,
+
PRINT,
ERR,
}
@@ 49,6 55,10 @@ impl From<OpCode> for u8 {
NOT => 0x11,
NEG => 0x12,
+
+ JMP => 0x13,
+ IF_NE => 0x14,
+
PRINT => 0xFE,
ERR => 0xFF
}
@@ 79,7 89,11 @@ impl From<u8> for OpCode {
0x11 => NOT,
0x12 => NEG,
- 0x13 => PRINT,
+
+ 0x13 => JMP,
+ 0x14 => IF_NE,
+
+ 0xFE => PRINT,
_ => ERR
}
}
M kabel/src/parser.rs => kabel/src/parser.rs +2 -1
@@ 238,8 238,9 @@ impl Parser {
if let TokenType::RightParen = right_paren.token_type {
let block = self.block()?;
if self.current < self.input.len() {
- let else_ident = self.read_token()?;
+ let else_ident = self.peek()?;
if let TokenType::Else = else_ident.token_type {
+ self.read_token()?;
if let TokenType::LeftBrace = self.peek()?.token_type {
let else_block = self.block()?;
return Ok(ast_from_token_ast!(
M kabel/src/vm.rs => kabel/src/vm.rs +26 -0
@@ 229,6 229,25 @@ impl VM {
return Err(wrong_type!(self, "Cannot negate bools"))
}
}
+ // JMP
+ 0x13 => {
+ let loc = self.read_u16();
+ self.ip += loc as usize;
+ }
+ // IF_NE
+ 0x14 => {
+ let condition = self.stack.pop().unwrap();
+ if let Value::Bool(condition) = condition {
+ if !condition {
+ let loc = self.read_u16();
+ self.ip += loc as usize;
+ } else {
+ self.read_u16();
+ }
+ } else {
+ return Err(wrong_type!(self, "if must have condition of type boolean"))
+ }
+ }
0xFE => { // PRINT
let value = self.stack.pop().unwrap();
@@ 249,6 268,13 @@ impl VM {
self.ip += 1;
byte
}
+ pub fn read_u16(&mut self) -> u16 {
+ let byte_one = (self.chunk[self.ip] as u16) << 0x08;
+ self.ip += 1;
+ let byte_two = self.chunk[self.ip] as u16;
+ self.ip += 1;
+ byte_one | byte_two
+ }
pub fn find_line(&mut self) -> usize { // returns line # at ip
let mut line_ip = 0;
for (line, rep) in self.lines.clone() {
M kabel_test/src/main.rs => kabel_test/src/main.rs +15 -3
@@ 1,13 1,22 @@
//use std::{env, fs};
-use kabel::{debug::{debug_ast, debug_token_array}, run_codegen, run_lexer, run_parser, run_semantic_analysis};
+use kabel::{debug::{debug_ast, debug_bytecode, debug_token_array}, run_codegen, run_lexer, run_parser, run_semantic_analysis};
fn main() {
/*let args: Vec<String> = env::args().collect();
let program =
fs::read_to_string(args[1].clone()).unwrap();*/
- let program = "print 4.1+5.3;".to_string();
+ let program =
+"if(true) {
+ print \"if\";
+} else if(true) {
+ print \"elseif\";
+} else {
+ print \"else\";
+}
+print \"after\";
+".to_string();
let mut output = "".to_string();
@@ 34,7 43,8 @@ fn main() {
println!("{}", output);
return;
}
- //output += &debug_ast(ast.clone(), 0);
+ output += &debug_ast(ast.clone(), 0);
+ output += "\n\n";
//output += &format!("{:#?}", ast);
let analyzer = run_semantic_analysis(program.to_string(), ast.clone());
for error in analyzer.errors.clone() {
@@ 45,6 55,8 @@ fn main() {
let codegen = run_codegen(program, ast);
let mut vm = codegen.vm;
+ output += &debug_bytecode(vm.chunk.clone());
+ output += "\n";
match vm.run(&mut output) {
Ok(()) => {}
Err(e) => output += &e.to_string(),