diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..36e2063 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,27 @@ +on: + release: + types: [created] + +jobs: + release: + name: release ${{ matrix.target }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-pc-windows-gnu + archive: zip + - target: x86_64-unknown-linux-musl + archive: tar.gz tar.xz tar.zst + - target: x86_64-apple-darwin + archive: zip + steps: + - uses: actions/checkout@master + - name: Compile and release + uses: rust-build/rust-build.action@v1.4.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + RUSTTARGET: ${{ matrix.target }} + ARCHIVE_TYPES: ${{ matrix.archive }} diff --git a/core/compiler/src/code.rs b/core/compiler/src/code.rs index d32446b..25a19cc 100644 --- a/core/compiler/src/code.rs +++ b/core/compiler/src/code.rs @@ -1,33 +1,31 @@ -use crate::CompiledInstructions; +use bytecoding::{Bytecode, DecodeError}; -pub const BYTE_ENDIAN: &'static str = "big"; -pub enum ByteEndianness { - Big, - Little, -} +pub type CompiledInstructions = Vec; -#[repr(u8)] -#[derive(Eq, PartialEq, Debug, Clone)] +#[derive(Eq, PartialEq, Debug, Clone, Bytecode)] +#[bytecode(type = u8)] pub enum Instruction { Illegal, - Call, + + Call(u16), + Return, ReturnValue, - Const, + Const(u16), Pop, - SetGlobal, - GetGlobal, - SetLocal, - GetLocal, + SetGlobal(u16), + GetGlobal(u16), + SetLocal(u16), + GetLocal(u16), - Array, - Hash, + Array(u16), + Hash(u16), Index, - Jump, - JumpNot, + Jump(u16), + JumpNot(u16), Add, Mul, @@ -36,8 +34,7 @@ pub enum Instruction { Mod, Pow, - True, - False, + Bool(#[bytecode(flatten_all = [true, false])] bool), Nil, @@ -52,93 +49,15 @@ pub enum Instruction { Negative, Not, - ShellCommand, -} - -impl Instruction { - pub fn from_u8(byte: u8) -> Self { - unsafe { ::std::mem::transmute(byte) } - } -} - -pub type Operand = usize; -pub type Operands = Vec; - -pub struct InstructionPacker(pub ByteEndianness); - -impl InstructionPacker { - fn unpack_16(&self, operand: u16) -> (u8, u8) { - let x1 = ((operand >> 8) & 0x00FF) as u8; - let x2 = (operand & 0x00FF) as u8; - - let InstructionPacker(endian) = self; - match endian { - ByteEndianness::Big => (x1, x2), - ByteEndianness::Little => (x2, x1), - } - } - - fn pack_u16(&self, x1: u8, x2: u8) -> u16 { - let InstructionPacker(endian) = self; - match endian { - ByteEndianness::Big => (((x1 as u16) & 0x00FF) << 8) | ((x2 as u16) & 0x00FF), - ByteEndianness::Little => (((x2 as u16) & 0x00FF) << 8) | ((x1 as u16) & 0x00FF), - } - } - - pub fn encode_instruction(&self, instruction: Instruction, operands: &Operands) -> Vec { - let operand_sizes = instruction.get_encoding_width(); - let mut statement = Vec::new(); - - statement.push(instruction as u8); - for idx in 0..operands.len() { - let operand = operands[idx]; - let width = operand_sizes[idx]; - - if width == 2 { - let (x1, x2) = self.unpack_16(operand as u16); - statement.push(x1); - statement.push(x2); - } else { - statement.push(operand as u8); - } - } - - return statement; - } - - pub fn decode_instruction( - &self, - instruction: &Instruction, - packed_ops: &[u8], - ) -> (Vec, usize) { - let operand_widths = instruction.get_encoding_width(); - let mut unpacked_stmt = vec![]; - let mut offset = 0; - - for width in operand_widths { - if width == 2 { - let operand_bytes = &packed_ops[offset..offset + 2]; - let packed_value = self.pack_u16(operand_bytes[0], operand_bytes[1]); - unpacked_stmt.push(packed_value as usize); - offset += 2; - } else { - unpacked_stmt.push(packed_ops[offset] as usize); - offset += 1; - } - } - - return (unpacked_stmt, offset); - } } impl Instruction { #[allow(dead_code)] pub fn as_string(&self) -> String { match self { - Self::Const => "OpConst".to_string(), + Self::Const(op) => format!("OpConst {op}"), Self::Pop => "OpPop".to_string(), - Self::Call => "OpCall".to_string(), + Self::Call(op) => format!("OpCall {op}"), Self::Return => "OpReturn".to_string(), Self::ReturnValue => "OpReturnValue".to_string(), Self::Add => "OpAdd".to_string(), @@ -147,8 +66,7 @@ impl Instruction { Self::Div => "OpDiv".to_string(), Self::Pow => "OpPow".to_string(), Self::Mod => "OpMod".to_string(), - Self::True => "OpTrue".to_string(), - Self::False => "OpFalse".to_string(), + Self::Bool(op) => format!("OpBool {op}"), Self::Gt => "OpGt".to_string(), Self::Lt => "OpLt".to_string(), Self::Eq => "OpEq".to_string(), @@ -159,85 +77,42 @@ impl Instruction { Self::Or => "OpOr".to_string(), Self::Negative => "OpNegative".to_string(), Self::Not => "OpNot".to_string(), - Self::Jump => "OpJump".to_string(), - Self::JumpNot => "OpJumpNot".to_string(), + Self::Jump(op) => format!("OpJump {op}"), + Self::JumpNot(op) => format!("OpJumpNot {op}"), Self::Nil => "OpNil".to_string(), - Self::GetGlobal => "OpGetGlobal".to_string(), - Self::SetGlobal => "OpSetGlobal".to_string(), - Self::GetLocal => "OpGetLocal".to_string(), - Self::SetLocal => "OpSetLocal".to_string(), - Self::Array => "OpArray".to_string(), - Self::Hash => "OpHash".to_string(), + Self::SetGlobal(op) => format!("OpSetGlobal {op}"), + Self::GetGlobal(op) => format!("OpGetGlobal {op}"), + Self::SetLocal(op) => format!("OpSetLocal {op}"), + Self::GetLocal(op) => format!("OpGetLocal {op}"), + Self::Array(op) => format!("OpArr {op}"), + Self::Hash(op) => format!("OpHash {op}"), Self::Index => "OpIndex".to_string(), - Self::ShellCommand => "OpCmd".to_string(), Self::Illegal => "ILLEGAL_OP".to_string(), } } - #[allow(dead_code)] - pub fn get_encoding_width(&self) -> Vec { - match self { - Self::Const - | Self::Jump - | Self::JumpNot - | Self::SetGlobal - | Self::GetGlobal - | Self::Array - | Self::Hash => vec![2], - Self::Call | Self::SetLocal | Self::GetLocal => vec![1], - Self::Pop - | Self::Add - | Self::Sub - | Self::Return - | Self::ReturnValue - | Self::Mul - | Self::Div - | Self::Mod - | Self::Pow - | Self::True - | Self::False - | Self::Lt - | Self::Gt - | Self::Lte - | Self::Gte - | Self::Eq - | Self::Neq - | Self::And - | Self::Or - | Self::Not - | Self::Negative - | Self::ShellCommand - | Self::Nil - | Self::Index - | Self::Illegal => vec![], - } + pub fn compile(&self) -> CompiledInstructions { + let mut buf = Vec::new(); + self.encode(&mut buf); + buf } - #[allow(dead_code)] - pub fn disasm_instruction(&self, operands: &Operands) -> String { - let op_strings: Vec = operands.into_iter().map(|op| format!("{:x}", op)).collect(); - let op_formatted = op_strings.join(", "); - let opcode = self.as_string(); - - return format!("{} {}", opcode, op_formatted); + pub fn compile_instructions(instructions: Vec) -> Vec { + let mut encoded = Vec::new(); + for ins in instructions { + ins.encode(&mut encoded); + } + encoded } pub fn decompile_instructions( - endian: ByteEndianness, instructions: &CompiledInstructions, - ) -> Vec<(Instruction, Vec)> { - let packer = InstructionPacker(endian); - let mut ip = 0 as usize; - let mut ins = vec![]; - while ip < instructions.len() { - let op: Instruction = Instruction::from_u8(instructions[ip]); - - let (operands, next_offset) = packer.decode_instruction(&op, &instructions[ip + 1..]); - - ins.push((op, operands)); - - ip += next_offset + 1; + ) -> Result, DecodeError> { + let mut buf: &[u8] = &instructions; + let mut ins = Vec::new(); + while !buf.is_empty() { + ins.push(Instruction::decode(&mut buf)?); } - ins + Ok(ins) } } diff --git a/core/compiler/src/objects.rs b/core/compiler/src/objects.rs index 531b63f..d4f938f 100644 --- a/core/compiler/src/objects.rs +++ b/core/compiler/src/objects.rs @@ -4,8 +4,6 @@ use std::{ hash::{Hash, Hasher}, }; -use pk_parser::ast::*; - use crate::CompiledInstructions; pub type BuiltinFunc = fn(Vec) -> Object; @@ -17,7 +15,6 @@ pub enum Object { Boolean(bool), Array(Vec), Hash(HashMap), - Func(Vec, BlockStatement), CompiledFunction { instructions: CompiledInstructions, locals: usize, @@ -60,7 +57,7 @@ impl fmt::Display for Object { .join(", ") ) } - Object::Func(..) | Object::CompiledFunction { .. } => { + Object::CompiledFunction { .. } => { write!(f, "[Function]") } Object::Builtin(..) => { @@ -87,9 +84,7 @@ impl Object { Object::Boolean(_) => "boolean".to_string(), Object::Array(_) => "array".to_string(), Object::Hash(_) => "hash".to_string(), - Object::Func(..) | Object::Builtin(..) | Object::CompiledFunction { .. } => { - "fn".to_string() - } + Object::Builtin(..) | Object::CompiledFunction { .. } => "fn".to_string(), Object::ReturnValue(_) => "return".to_string(), Object::Error(_) => "error".to_string(), Object::Nil => "nil".to_string(), diff --git a/core/compiler/src/symbols_table.rs b/core/compiler/src/symbols_table.rs index a7d8967..f731a17 100644 --- a/core/compiler/src/symbols_table.rs +++ b/core/compiler/src/symbols_table.rs @@ -49,10 +49,10 @@ impl SymbolTable { scope, name: name.to_string(), }; - self.store.insert(name.to_string(), symbol.clone()); if !self.store.contains_key(&name.to_string()) { self.num_defs += 1; } + self.store.insert(name.to_string(), symbol.clone()); symbol } diff --git a/core/lexer/src/tokens.rs b/core/lexer/src/tokens.rs index 5956f71..193327a 100644 --- a/core/lexer/src/tokens.rs +++ b/core/lexer/src/tokens.rs @@ -56,8 +56,6 @@ pub enum RawToken { Minus, #[token("!")] Bang, - #[token("$")] - Dollar, #[token("*")] Asterisk, #[token("/")] diff --git a/core/parser/src/ast.rs b/core/parser/src/ast.rs index 06f4725..f45867d 100644 --- a/core/parser/src/ast.rs +++ b/core/parser/src/ast.rs @@ -54,7 +54,6 @@ pub enum Prefix { Plus, Minus, Not, - Dollar, } #[derive(PartialEq, Clone, Debug)] diff --git a/core/parser/src/lib.rs b/core/parser/src/lib.rs index 0db18b9..33052c9 100644 --- a/core/parser/src/lib.rs +++ b/core/parser/src/lib.rs @@ -86,9 +86,7 @@ impl Parser { RawToken::String(_) => self.parse_string_expr(), RawToken::Nil => self.parse_nil_expr(), RawToken::True | RawToken::False => self.parse_boolean_expr(), - RawToken::Plus | RawToken::Bang | RawToken::Minus | RawToken::Dollar => { - self.parse_prefix_expr() - } + RawToken::Plus | RawToken::Bang | RawToken::Minus => self.parse_prefix_expr(), RawToken::ParenL => self.parse_grouped_expr(), RawToken::BracketL => self.parse_array_expr(), RawToken::BraceL => self.parse_hash_expr(), @@ -449,17 +447,12 @@ impl Parser { RawToken::Bang => Prefix::Not, RawToken::Minus => Prefix::Minus, RawToken::Plus => Prefix::Plus, - RawToken::Dollar => Prefix::Dollar, _ => return None, }; self.step(); - match self.parse_expression(if current == RawToken::Dollar { - Precedence::Highest - } else { - Precedence::Prefix - }) { + match self.parse_expression(Precedence::Prefix) { Some(expr) => Some(Expression::Prefix(prefix, Box::new(expr))), None => None, } diff --git a/core/vm/src/context.rs b/core/vm/src/context.rs index bf0ba66..5ede92e 100644 --- a/core/vm/src/context.rs +++ b/core/vm/src/context.rs @@ -1,7 +1,7 @@ -use std::{cell::RefCell, collections::HashMap, ops::Range, process::Command, rc::Rc}; +use std::{cell::RefCell, collections::HashMap, ops::Range, rc::Rc}; use pk_compiler::{ - code::{Instruction, Operands}, + code::Instruction, objects::{Float, Object}, symbols_table::ConstantsPool, }; @@ -21,13 +21,13 @@ pub struct VMContext<'a> { } impl<'a> VMContext<'a> { - pub fn exec_instruction(&mut self, op: &Instruction, operands: &Operands) -> Option { - match op { - Instruction::Const => self.exec_const(&operands), - Instruction::SetGlobal => self.exec_set_global(&operands), - Instruction::GetGlobal => self.exec_get_global(&operands), - Instruction::SetLocal => self.exec_set_local(&operands), - Instruction::GetLocal => self.exec_get_local(&operands), + pub fn exec_instruction(&mut self, ins: &Instruction) -> Option { + match ins { + Instruction::Const(op) => self.exec_const(*op), + Instruction::SetGlobal(op) => self.exec_set_global(*op), + Instruction::GetGlobal(op) => self.exec_get_global(*op), + Instruction::SetLocal(op) => self.exec_set_local(*op), + Instruction::GetLocal(op) => self.exec_get_local(*op), Instruction::Add | Instruction::Sub | Instruction::Mul @@ -41,41 +41,37 @@ impl<'a> VMContext<'a> { | Instruction::Lte | Instruction::Gte | Instruction::And - | Instruction::Or => self.exec_binary_op(&op), - Instruction::True | Instruction::False | Instruction::Nil => { - self.exec_booleans_objects(&op) - } + | Instruction::Or => self.exec_binary_op(ins), + Instruction::Bool(_) | Instruction::Nil => self.exec_booleans_objects(ins), Instruction::Not => self.exec_not(), Instruction::Negative => self.exec_negative(), - Instruction::ShellCommand => self.exec_shell_command(), Instruction::Pop => self.exec_pop(), - Instruction::Jump | Instruction::JumpNot => self.exec_jumps(&op, &operands), - Instruction::Array => self.exec_array(&operands), - Instruction::Hash => self.exec_hash(&operands), + Instruction::Jump(_) | Instruction::JumpNot(_) => self.exec_jumps(ins), + Instruction::Array(op) => self.exec_array(*op), + Instruction::Hash(op) => self.exec_hash(*op), Instruction::Index => self.exec_index(), - Instruction::Call => self.exec_call(&operands), - Instruction::Return | Instruction::ReturnValue => self.exec_returns(&op), + Instruction::Call(op) => self.exec_call(*op), + Instruction::Return | Instruction::ReturnValue => self.exec_returns(ins), #[allow(unreachable_patterns)] _ => { return Some(VMError::new( VMErrorKind::IllegalOperation, - format!("instruction \"{}\" not implemented", op.as_string(),), + format!("instruction \"{}\" not implemented", ins.as_string(),), )); } } } - fn exec_const(&mut self, operands: &Operands) -> Option { - let obj = self.constants.get_object(operands[0]); + fn exec_const(&mut self, operand: u16) -> Option { + let obj = self.constants.get_object(operand as usize); self.data_stack.push_object(obj.unwrap()) } - fn exec_set_global(&mut self, operands: &Operands) -> Option { - self.call_stack.top().ip += 2; - self.globals[operands[0]] = Some(self.data_stack.pop_object().unwrap().as_ref().clone()); + fn exec_set_global(&mut self, operand: u16) -> Option { + self.globals[operand as usize] = + Some(self.data_stack.pop_object().unwrap().as_ref().clone()); None } - fn exec_get_global(&mut self, operands: &Operands) -> Option { - self.call_stack.top().ip += 2; - match &self.globals[operands[0]] { + fn exec_get_global(&mut self, operand: u16) -> Option { + match &self.globals[operand as usize] { Some(obj) => { if let Some(err) = self.data_stack.push_object(Rc::new(obj.clone())) { return Some(err.clone()); @@ -87,20 +83,17 @@ impl<'a> VMContext<'a> { } None } - fn exec_set_local(&mut self, operands: &Operands) -> Option { - self.call_stack.top().ip += 2; + fn exec_set_local(&mut self, operand: u16) -> Option { let frame = self.call_stack.top_ref().clone(); - self.data_stack.stack[(frame.bp - 1) as usize + operands[0]] = - self.data_stack.pop_object().unwrap(); + self.data_stack.stack[(frame.bp) as usize + operand as usize] = self.data_stack.top(); None } - fn exec_get_local(&mut self, operands: &Operands) -> Option { - self.call_stack.top().ip += 2; + fn exec_get_local(&mut self, operands: u16) -> Option { let frame = self.call_stack.top_ref().clone(); match self .data_stack .stack - .get((frame.bp - 1) as usize + operands[0]) + .get((frame.bp) as usize + operands as usize) .cloned() { Some(obj) => { @@ -131,7 +124,13 @@ impl<'a> VMContext<'a> { self.exec_binary_numeric_op(&op, &left, &right); None } - (String(left), String(right), _) => self + (String(left), String(right), Instruction::Add) => self + .data_stack + .push_object(Rc::new(Object::String(format!("{}{}", left, right)))), + (String(left), right, Instruction::Add) => self + .data_stack + .push_object(Rc::new(Object::String(format!("{}{}", left, right)))), + (left, String(right), Instruction::Add) => self .data_stack .push_object(Rc::new(Object::String(format!("{}{}", left, right)))), (left, right, Instruction::Add) => self @@ -210,11 +209,8 @@ impl<'a> VMContext<'a> { } fn exec_booleans_objects(&mut self, op: &Instruction) -> Option { match op { - Instruction::True => { - self.data_stack.push_object(Rc::new(Object::Boolean(true))); - } - Instruction::False => { - self.data_stack.push_object(Rc::new(Object::Boolean(false))); + Instruction::Bool(val) => { + self.data_stack.push_object(Rc::new(Object::Boolean(*val))); } Instruction::Nil => { self.data_stack.push_object(Rc::new(Object::Nil)); @@ -228,17 +224,17 @@ impl<'a> VMContext<'a> { self.data_stack.pop_object(); None } - fn exec_jumps(&mut self, op: &Instruction, operands: &Operands) -> Option { + fn exec_jumps(&mut self, op: &Instruction) -> Option { match op { - Instruction::Jump => { - self.call_stack.top().ip = operands[0] - 1; + Instruction::Jump(idx) => { + self.call_stack.top().ip = *idx as usize - 1; } - Instruction::JumpNot => { - self.call_stack.top().ip += 2; + Instruction::JumpNot(idx) => { + // self.call_stack.top().ip += 2; let obj = self.data_stack.pop_object(); match *obj.unwrap() { Object::Boolean(false) | Object::Nil => { - self.call_stack.top().ip = operands[0] - 1; + self.call_stack.top().ip = *idx as usize - 1; } _ => {} } @@ -282,65 +278,16 @@ impl<'a> VMContext<'a> { }; None } - fn exec_shell_command(&mut self) -> Option { - let obj = self.data_stack.pop_object(); - if obj.is_some() { - match &*obj.unwrap() { - Object::String(cmd) => { - let mut hash: HashMap = HashMap::new(); - let splited: Vec<&str> = cmd.split_whitespace().collect(); - let mut cmd = &mut Command::new(splited.get(0).unwrap_or(&"")); - if splited.len() > 1 { - cmd = cmd.args(&splited[1..]); - } - let output = cmd.output(); - match output { - Ok(output) => { - hash.insert( - Object::String("stdout".to_string()), - Object::String(String::from_utf8_lossy(&output.stdout).to_string()), - ); - hash.insert( - Object::String("stderr".to_string()), - Object::String(String::from_utf8_lossy(&output.stderr).to_string()), - ); - hash.insert( - Object::String("status".to_string()), - Object::Number(Float(output.status.code().unwrap_or(0) as f64)), - ); - self.data_stack.push_object(Rc::new(Object::Hash(hash))); - } - Err(err) => { - return Some(VMError::new(VMErrorKind::ShellCommand, err.to_string())) - } - } - } - obj => { - return Some(VMError::new( - VMErrorKind::IllegalOperation, - format!( - "cannot exec \"{}\" object type as shell command", - obj.type_str() - ), - )) - } - } - }; - None - } - fn exec_array(&mut self, operands: &Operands) -> Option { - self.call_stack.top().ip += 2; - dbg!(self.data_stack.stack_pointer, &operands[0]); + fn exec_array(&mut self, operand: u16) -> Option { let array = self.build_array(Range { - start: self.data_stack.stack_pointer as usize - operands[0], + start: self.data_stack.stack_pointer as usize - operand as usize, end: self.data_stack.stack_pointer as usize, }); self.data_stack.push_object(array) } - fn exec_hash(&mut self, operands: &Operands) -> Option { - self.call_stack.top().ip += 2; + fn exec_hash(&mut self, operand: u16) -> Option { let hash = self.build_hash(Range { - start: self.data_stack.stack_pointer as usize - operands[0], + start: self.data_stack.stack_pointer as usize - operand as usize, end: self.data_stack.stack_pointer as usize, }); // self.call_stack.stack_pointer -= operands[0] as i64; @@ -378,20 +325,19 @@ impl<'a> VMContext<'a> { self.data_stack .push_object(Rc::new(hash.get(index).unwrap_or(&Object::Nil).clone())) } - fn exec_call(&mut self, operands: &Operands) -> Option { - let obj = &*self.data_stack.top_offset(operands[0]); - // dbg!(&self.data_stack.stack, obj, operands); + fn exec_call(&mut self, operand: u16) -> Option { + let obj = &*self.data_stack.top_offset(operand as usize); match obj { Object::CompiledFunction { instructions, locals, } => { - let bp = self.data_stack.stack_pointer; - let frame = Frame::new(instructions.clone(), bp); + let base_pointer = self.data_stack.stack_pointer; + let frame = Frame::new(instructions.clone(), base_pointer - operand as i64); match self.call_stack.push_frame(RefCell::new(frame)) { Err(err) => Some(err), _ => { - self.data_stack.stack_pointer = bp + *locals as i64; + self.data_stack.stack_pointer = base_pointer + *locals as i64; None } } @@ -402,6 +348,7 @@ impl<'a> VMContext<'a> { )), } } + fn exec_returns(&mut self, op: &Instruction) -> Option { match op { Instruction::Return => { @@ -411,7 +358,7 @@ impl<'a> VMContext<'a> { } let frame = current_frame_res.unwrap(); - self.data_stack.stack_pointer = frame.borrow().bp - 1; + self.data_stack.stack_pointer = frame.borrow().bp; self.data_stack.push_object(Rc::new(Object::Nil)) } Instruction::ReturnValue => { @@ -429,7 +376,7 @@ impl<'a> VMContext<'a> { let bp = frame.borrow().bp; self.data_stack.stack.truncate(bp as usize); - self.data_stack.stack_pointer = frame.borrow().bp - 1; + self.data_stack.stack_pointer = frame.borrow().bp; self.data_stack.push_object(return_value) } _ => None, diff --git a/core/vm/src/frames.rs b/core/vm/src/frames.rs index 603a2bb..7045d6a 100644 --- a/core/vm/src/frames.rs +++ b/core/vm/src/frames.rs @@ -1,11 +1,8 @@ -use pk_compiler::{ - code::{ByteEndianness, Instruction, InstructionPacker, Operands}, - CompiledInstructions, -}; +use pk_compiler::code::{CompiledInstructions, Instruction}; #[derive(Debug, Clone)] pub struct Frame { - pub instructions: CompiledInstructions, + pub instructions: Vec, pub ip: usize, pub bp: i64, } @@ -13,7 +10,7 @@ pub struct Frame { impl Frame { pub fn new(instructions: CompiledInstructions, bp: i64) -> Self { Self { - instructions, + instructions: Instruction::decompile_instructions(&instructions).unwrap(), ip: 0, bp, } @@ -26,34 +23,19 @@ impl Frame { self.ip = cb(self.ip); } - pub fn forward_ip(&mut self, op: &Instruction, offset: usize) { + pub fn forward_ip(&mut self, op: &Instruction) { match op { - Instruction::SetGlobal - | Instruction::GetGlobal - | Instruction::SetLocal - | Instruction::GetLocal - | Instruction::Jump - | Instruction::JumpNot - | Instruction::Array - | Instruction::Hash => { - self.ip += 1; - } - Instruction::Call => { + Instruction::Call(_) => { self.ip = 0; } _ => { - self.ip += offset + 1; + self.ip += 1; } } } - pub fn read_current_instruction(&self) -> (Instruction, Operands, usize) { - let decoded_opcode: Instruction = Instruction::from_u8(self.instructions[self.ip]); - - let (operands, next_offset) = InstructionPacker(ByteEndianness::Big) - .decode_instruction(&decoded_opcode, &self.instructions[self.ip + 1..]); - - return (decoded_opcode, operands, next_offset); + pub fn read_current_instruction(&self) -> Instruction { + self.instructions[self.ip].clone() } pub fn has_instructions(&self) -> bool { diff --git a/core/vm/src/lib.rs b/core/vm/src/lib.rs index 0a7e9a0..647dbba 100644 --- a/core/vm/src/lib.rs +++ b/core/vm/src/lib.rs @@ -70,21 +70,19 @@ impl<'a> VM<'a> { if push_result.is_err() { return Some(push_result.unwrap_err()); } - - // dbg!(&self.call_stack); return None; } pub fn eval_from_context(ctx: &mut VMContext) -> Result, VMError> { while ctx.call_stack.top_ref().has_instructions() { - let (op, operands, next_offset) = ctx.call_stack.top_ref().read_current_instruction(); + let ins = ctx.call_stack.top_ref().read_current_instruction(); - match ctx.exec_instruction(&op, &operands) { + match ctx.exec_instruction(&ins) { Some(err) => return Err(err), _ => {} }; - ctx.call_stack.top().forward_ip(&op, next_offset); + ctx.call_stack.top().forward_ip(&ins); } match &ctx.data_stack.last_popped { Some(popped_result) => Ok(popped_result.clone()), diff --git a/core/vm/src/stack.rs b/core/vm/src/stack.rs index 4e9fb9c..1388e9a 100644 --- a/core/vm/src/stack.rs +++ b/core/vm/src/stack.rs @@ -111,7 +111,6 @@ impl DataStack { self.stack.last().unwrap().clone() } pub fn top_offset(&mut self, offset: usize) -> Rc { - dbg!(self.stack.len(), offset); self.stack .get((self.stack.len() - offset) - 1) .unwrap() diff --git a/examples/fibonacci.pk b/examples/fibonacci.pk index 77e144a..11411df 100644 --- a/examples/fibonacci.pk +++ b/examples/fibonacci.pk @@ -10,5 +10,5 @@ let fib = fn(x) { } } -// Must print 55 +# Must print 55 println(fib(10)) diff --git a/palacinke/src/bin/cmd/build.rs b/palacinke/src/bin/cmd/build.rs new file mode 100644 index 0000000..cfe1d75 --- /dev/null +++ b/palacinke/src/bin/cmd/build.rs @@ -0,0 +1,59 @@ +use std::fs; + +use palacinke::utils::*; +use pk_compiler::{ + symbols_table::{ConstantsPool, SymbolTable}, + BytecodeDecompiler, CompiledBytecode, Compiler, +}; +use pk_parser::Parser; + +pub fn build(path: &str, output: Option) { + let output = match output { + Some(output) => output.to_string(), + _ => { + let val = format!("{}.b", path); + val + } + }; + let buff = match fs::read(path) { + Ok(buff) => buff, + Err(err) => { + println!("{}", err); + return; + } + }; + let source = String::from_utf8_lossy(&buff); + match compile(&source) { + Some(bytecode) => { + if output.ends_with(".pks") { + fs::write(output, BytecodeDecompiler::disassemble(&bytecode)) + .expect("Unable to write file"); + } else { + fs::write(output, &bytecode.instructions).expect("Unable to write file"); + } + } + _ => {} + } +} + +fn compile(source: &str) -> Option { + let mut parser = Parser::from_source(&source); + let module = match parser.parse() { + Ok(module) => module, + Err(errors) => { + print_parse_errors(errors); + return None; + } + }; + let mut symbols_table = SymbolTable::new(); + let mut constants = ConstantsPool::new(); + let mut compiler = Compiler::new(&mut symbols_table, &mut constants); + let bytecode = match compiler.compile(module) { + Ok(bytecode) => bytecode, + Err(err) => { + print_compilation_error(err); + return None; + } + }; + Some(bytecode) +} diff --git a/palacinke/src/bin/cmd/mod.rs b/palacinke/src/bin/cmd/mod.rs index adfefd5..211c1dd 100644 --- a/palacinke/src/bin/cmd/mod.rs +++ b/palacinke/src/bin/cmd/mod.rs @@ -1,5 +1,6 @@ use clap::{command, Parser, Subcommand}; +mod build; mod repl; mod run; @@ -16,6 +17,18 @@ enum Commands { #[command(about = "Eval a script")] Run { path: String }, + #[command(about = "Compile a script to bytecode", arg_required_else_help = true)] + Build { + #[arg(required = true)] + path: String, + #[arg( + long, + short = 'o', + help = "if the output extension ends with \".pks\" the script will be compiled to IR instead" + )] + output: Option, + }, + #[command(about = "Enter to the repl mode")] Repl, } @@ -27,6 +40,9 @@ pub fn start() { Commands::Run { path } => { run::run(&path); } + Commands::Build { path, output } => { + build::build(&path, output.clone()); + } Commands::Repl => { repl::start(); } diff --git a/palacinke/src/bin/cmd/repl.rs b/palacinke/src/bin/cmd/repl.rs index 3a992ec..8a60386 100644 --- a/palacinke/src/bin/cmd/repl.rs +++ b/palacinke/src/bin/cmd/repl.rs @@ -3,7 +3,7 @@ use std::fs; use colored::Colorize; use palacinke::utils::*; use pk_compiler::symbols_table::{ConstantsPool, SymbolTable}; -use pk_compiler::Compiler; +use pk_compiler::{BytecodeDecompiler, Compiler}; use pk_parser::ast::Module; use pk_parser::Parser; use pk_vm::{new_globals_store, GlobalsStore, VM}; @@ -37,9 +37,16 @@ pub fn start() { match readline { Ok(line) => { rl.add_history_entry(line.as_str()); - let mut parser = Parser::from_source(&line.as_str()); + let source = &line.as_str().strip_prefix(":ir ").unwrap_or(&line.as_str()); + let mut parser = Parser::from_source(source); match parser.parse() { - Ok(module) => eval(module, &mut symbols_table, &mut constants, &mut globals), + Ok(module) => { + if line.as_str().starts_with(":ir ") { + disasm(module, &mut symbols_table, &mut constants); + } else { + eval(module, &mut symbols_table, &mut constants, &mut globals) + } + } Err(errors) => { print_parse_errors(errors); } @@ -62,6 +69,18 @@ pub fn start() { let _ = rl.save_history(history.as_path()); } +fn disasm(module: Module, symbols_table: &mut SymbolTable, constants: &mut ConstantsPool) { + let fallback = (constants.clone(), symbols_table.clone()); + let mut compiler = Compiler::new(symbols_table, constants); + match compiler.compile(module) { + Ok(bytecode) => { + println!("{}", BytecodeDecompiler::disassemble(&bytecode)); + } + Err(err) => print_compilation_error(err), + } + (*constants, *symbols_table) = fallback; +} + fn eval( module: Module, symbols_table: &mut SymbolTable,