diff --git a/src/ast_compiler.rs b/src/ast_compiler.rs index c09c038..7df9d0f 100644 --- a/src/ast_compiler.rs +++ b/src/ast_compiler.rs @@ -228,6 +228,9 @@ impl AstCompiler { } fn let_declaration(&mut self) -> Result { + if self.peek().token_type.is_type(){ + return Err(CompilerError::KeywordNotAllowedAsIdentifier(self.peek().token_type)) + } let name_token = self.consume(Identifier, Expected("variable name."))?; let declared_type = if self.check(Colon) { diff --git a/src/bytecode_compiler.rs b/src/bytecode_compiler.rs index 2556be0..044fcd6 100644 --- a/src/bytecode_compiler.rs +++ b/src/bytecode_compiler.rs @@ -1,16 +1,22 @@ use crate::ast_compiler::{Expression, Function, Statement}; use crate::chunk::Chunk; +use crate::errors::CompilerError; use crate::tokens::TokenType; use crate::value::Value; -use crate::vm::{OP_ADD, OP_AND, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_CHAR, OP_DEF_DATE, OP_DEF_F32, OP_DEF_F64, OP_DEF_I32, OP_DEF_I64, OP_DEF_LIST, OP_DEF_MAP, OP_DEF_STRING, OP_DEF_STRUCT, OP_DEFINE, OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_OR, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT, OP_DEF_U32}; +use crate::vm::{ + OP_ADD, OP_AND, OP_ASSIGN, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_BOOL, + OP_DEF_CHAR, OP_DEF_DATE, OP_DEF_F32, OP_DEF_F64, OP_DEF_I32, OP_DEF_I64, OP_DEF_LIST, + OP_DEF_MAP, OP_DEF_STRING, OP_DEF_STRUCT, OP_DEF_U32, OP_DEFINE, OP_DIVIDE, OP_EQUAL, OP_GET, + OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_OR, + OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT, +}; use std::collections::HashMap; -use crate::errors::CompilerError; pub fn compile( namespace: Option<&str>, ast: &Vec, registry: &mut HashMap, -) -> Result<(),CompilerError> { +) -> Result<(), CompilerError> { compile_name(ast, namespace, registry) } @@ -22,9 +28,10 @@ pub(crate) fn compile_function( let mut compiler = Compiler::new(&function.name.lexeme); for parm in &function.parameters { let name = parm.name.lexeme.clone(); - let name_index = compiler.chunk.add_constant(Value::String(name.clone())); - compiler.vars.insert(name, name_index); - compiler.emit_bytes(OP_DEFINE, name_index as u16); + let var_index = compiler.chunk.add_var(&parm.var_type, &parm.name.lexeme); + + compiler.vars.insert(name, var_index); + // compiler.emit_bytes(OP_DEFINE, name_index as u16); } Ok(compiler.compile(&function.body, registry, namespace)?) @@ -34,11 +41,11 @@ pub(crate) fn compile_name( ast: &Vec, namespace: Option<&str>, registry: &mut HashMap, -) -> Result<(),CompilerError> { - let name=namespace.unwrap_or("main"); +) -> Result<(), CompilerError> { + let name = namespace.unwrap_or("main"); let compiler = Compiler::new(name); let chunk = compiler.compile(ast, registry, name)?; - let qname = if let Some(namespace) = namespace{ + let qname = if let Some(namespace) = namespace { format!("{}.{}", namespace, "main") } else { "main".to_string() @@ -70,17 +77,6 @@ impl Compiler { registry: &mut HashMap, namespace: &str, ) -> Result { - //TODO can likely be removed - for statement in ast { - if let Statement::FunctionStmt { function } = statement { - self.emit_constant(Value::String(format!( - "{}.{}", - namespace, - function.name.lexeme.clone() - ))); - } - } - for statement in ast { self.compile_statement(statement, registry, namespace)?; } @@ -102,10 +98,10 @@ impl Compiler { var_type, initializer, } => { - let name_index = self.chunk.add_constant(Value::String(name.lexeme.clone())); + let name_index = self.chunk.add_var(var_type, &name.lexeme); self.vars.insert(name.lexeme.clone(), name_index); self.compile_expression(namespace, initializer, registry)?; - self.define_variable(var_type, name_index, &initializer)?; + self.emit_bytes(OP_ASSIGN, name_index as u16); } Statement::PrintStmt { value } => { self.compile_expression(namespace, value, registry)?; @@ -116,7 +112,7 @@ impl Compiler { } Statement::FunctionStmt { function } => { let function_name = function.name.lexeme.clone(); - self.emit_constant(Value::String(function_name.clone())); + // self.emit_constant(Value::String(function_name.clone())); let compiled_function = compile_function(function, registry, namespace)?; registry.insert( format!("{}.{}", self.chunk.name, function_name), @@ -140,7 +136,7 @@ impl Compiler { Expression::FunctionCall { name, arguments, .. } => { - let qname=format!("{}.{}", namespace, name); + let qname = format!("{}.{}", namespace, name); let name_index = self .chunk .find_constant(&qname) @@ -163,12 +159,14 @@ impl Compiler { for expr in values { self.compile_expression(namespace, expr, registry)?; } + self.emit_bytes(OP_DEF_LIST, values.len() as u16); } Expression::Map { entries, .. } => { for (key, value) in entries { self.compile_expression(namespace, key, registry)?; self.compile_expression(namespace, value, registry)?; } + self.emit_bytes(OP_DEF_MAP, entries.len() as u16); } Expression::Grouping { expression, .. } => { self.compile_expression(namespace, expression, registry)? @@ -219,42 +217,6 @@ impl Compiler { Ok(()) } - fn define_variable( - &mut self, - var_type: &TokenType, - name_index: usize, - initializer: &Expression, - ) -> Result<(), CompilerError> { - let def_op = match var_type { - TokenType::I32 => OP_DEF_I32, - TokenType::I64 => OP_DEF_I64, - TokenType::U32 => OP_DEF_U32, - TokenType::U64 => OP_DEF_I64, - TokenType::F32 => OP_DEF_F32, - TokenType::F64 => OP_DEF_F64, - TokenType::Date => OP_DEF_DATE, - TokenType::StringType => OP_DEF_STRING, - TokenType::Char => OP_DEF_CHAR, - TokenType::Bool => OP_DEF_BOOL, - TokenType::ListType => OP_DEF_LIST, - TokenType::MapType => OP_DEF_MAP, - TokenType::Object => OP_DEF_STRUCT, - _ => unimplemented!("{}", var_type), - }; - - self.emit_bytes(def_op, name_index as u16); - match initializer { - Expression::List { values, .. } => { - self.emit_byte(values.len() as u16); - } - Expression::Map { entries, .. } => { - self.emit_byte(entries.len() as u16); - } - _ => {} - } - Ok(()) - } - fn emit_byte(&mut self, byte: u16) { self.chunk.add(byte, self.current_line); } diff --git a/src/chunk.rs b/src/chunk.rs index 9d0d05b..cb19807 100644 --- a/src/chunk.rs +++ b/src/chunk.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; use crate::ast_compiler::Parameter; +use crate::tokens::TokenType; use crate::value::Value; use crate::vm::{ OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_F32, @@ -14,7 +15,8 @@ pub struct Chunk { pub code: Vec, pub constants: Vec, lines: Vec, - object_defs: HashMap> + object_defs: HashMap>, + pub vars: Vec<(TokenType, String)> } impl Chunk { @@ -38,6 +40,7 @@ impl Chunk { constants: vec![], lines: vec![], object_defs: HashMap::new(), + vars: vec![] } } @@ -51,6 +54,11 @@ impl Chunk { self.constants.len() - 1 } + pub(crate) fn add_var(&mut self, var_type: &TokenType, name: &str) -> usize { + self.vars.push((var_type.clone(), name.to_string())); + self.vars.len() - 1 + } + pub (crate) fn add_object_def(&mut self, name: &str, fields: &[Parameter]){ self.object_defs.insert(name.to_string(), fields.to_vec()); } diff --git a/src/compiler_tests.rs b/src/compiler_tests.rs index 966f32f..3fba686 100644 --- a/src/compiler_tests.rs +++ b/src/compiler_tests.rs @@ -29,19 +29,19 @@ mod tests { } #[test] - fn let_infer_type() { + fn infer_type() { assert_eq!(run(r#"let a=1 a"#), Ok(Value::I64(1))); } #[test] - fn let_u32() { + fn define_u32() { assert_eq!(run(r#"let a:u32=1 a"#), Ok(Value::U32(1))); } #[test] - fn let_char() { + fn define_char() { assert_eq!( run(r#"let a:char='a' a"#), @@ -50,7 +50,7 @@ a"#), } #[test] - fn let_u32_invalid_value_negative() { + fn define_u32_invalid_value_negative() { let r = compile("let a:u32=-1"); assert!(r.is_err()); if let Err(e) = &r { @@ -62,7 +62,7 @@ a"#), } #[test] - fn let_u64_invalid_value_negative() { + fn define_u64_invalid_value_negative() { let r = compile("let a:u64=-1"); assert!(r.is_err()); if let Err(e) = &r { @@ -97,7 +97,7 @@ add_hello("world")"#,), } #[test] - fn object_definition() { + fn define_object() { let r = compile( r#" object Person: @@ -120,7 +120,7 @@ object Person: // } #[test] - fn let_map() { + fn literal_map() { let result = run(r#"{"name": "Dent", "age": 40 }"#); assert!(result.is_ok()); let result = result.unwrap(); @@ -131,8 +131,29 @@ object Person: ); assert_eq!( map.get(&Value::String("age".to_string())).unwrap(), - &Value::I32(40) + &Value::I64(40) ); } } + + #[test] + fn define_map() { + let result = run(r#"let m = {"name": "Dent"} +m"#); + + let result = result.unwrap(); + if let Value::Map(map) = result { + assert_eq!( + map.get(&Value::String("name".to_string())).unwrap(), + &Value::String("Dent".to_string()) + ); + } + } + + #[test] + fn keyword_error(){ + let result = run(r#"let map = {"name": "Dent"}"#); + assert!(result.is_err()); + assert_eq!("'map' is a keyword. You cannot use it as an identifier",result.unwrap_err().to_string()); + } } diff --git a/src/errors.rs b/src/errors.rs index 0fa85e8..df13ea4 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,5 +1,5 @@ -use thiserror::Error; use crate::tokens::{Token, TokenType}; +use thiserror::Error; #[derive(Error, Debug, PartialEq)] pub enum Error { @@ -11,7 +11,6 @@ pub enum Error { Platform(String), } - #[derive(Error, Debug, PartialEq)] pub enum CompilerError { #[error("Compilation failed")] @@ -21,7 +20,7 @@ pub enum CompilerError { #[error("Expected {0}")] Expected(&'static str), #[error("unexpected indent level {0} vs expected {1}")] - UnexpectedIndent(usize,usize), + UnexpectedIndent(usize, usize), #[error("Type mismatch at line {0}: {1}")] TypeError(usize, Box), #[error("Uninitialized variables are not allowed.")] @@ -39,7 +38,9 @@ pub enum CompilerError { #[error("Illegal char length for {0} at line {1}")] IllegalCharLength(String, usize), #[error("Unexpected type {0}")] - UnexpectedType(TokenType) + UnexpectedType(TokenType), + #[error("'{0}' is a keyword. You cannot use it as an identifier")] + KeywordNotAllowedAsIdentifier(TokenType), } #[derive(Error, Debug, PartialEq)] @@ -58,4 +59,6 @@ pub enum ValueError { CannotAnd(&'static str), #[error("{0}")] Some(&'static str), -} \ No newline at end of file + #[error("Illegal cast")] + IllegalCast, +} diff --git a/src/value.rs b/src/value.rs index 30f4dc4..a3ec450 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,10 +1,10 @@ +use crate::errors::ValueError; use chrono::{DateTime, Utc}; use std::cmp::Ordering; use std::collections::HashMap; use std::fmt::{Display, Formatter}; use std::hash::{Hash, Hasher}; use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Shl, Shr, Sub}; -use crate::errors::ValueError; #[derive(Debug, Clone)] pub struct Object { @@ -32,6 +32,56 @@ pub enum Value { Void, } +impl Value { + pub fn cast_u32(self) -> Result { + match self { + Value::U32(v) => Ok(Value::U32(v)), + Value::U64(v) => Ok(Value::U32(v as u32)), + Value::I32(v) => Ok(Value::U32(v as u32)), + Value::I64(v) => Ok(Value::U32(v as u32)), + Value::F32(v) => Ok(Value::U32(v as u32)), + Value::F64(v) => Ok(Value::U32(v as u32)), + _ => Err(ValueError::IllegalCast), + } + } + + pub fn cast_u64(self) -> Result { + match self { + Value::U32(v) => Ok(Value::U64(v as u64)), + Value::U64(v) => Ok(Value::U64(v)), + Value::I32(v) => Ok(Value::U64(v as u64)), + Value::I64(v) => Ok(Value::U64(v as u64)), + Value::F32(v) => Ok(Value::U64(v as u64)), + Value::F64(v) => Ok(Value::U64(v as u64)), + _ => Err(ValueError::IllegalCast), + } + } + + pub fn cast_i32(self) -> Result { + match self { + Value::U32(v) => Ok(Value::I32(v as i32)), + Value::U64(v) => Ok(Value::I32(v as i32)), + Value::I32(v) => Ok(Value::I32(v)), + Value::I64(v) => Ok(Value::I32(v as i32)), + Value::F32(v) => Ok(Value::I32(v as i32)), + Value::F64(v) => Ok(Value::I32(v as i32)), + _ => Err(ValueError::IllegalCast), + } + } + + pub fn cast_f32(self) -> Result { + match self { + Value::U32(v) => Ok(Value::F32(v as f32)), + Value::U64(v) => Ok(Value::F32(v as f32)), + Value::I32(v) => Ok(Value::F32(v as f32)), + Value::I64(v) => Ok(Value::F32(v as f32)), + Value::F32(v) => Ok(Value::F32(v)), + Value::F64(v) => Ok(Value::F32(v as f32)), + _ => Err(ValueError::IllegalCast), + } + } +} + impl Into for i32 { fn into(self) -> Value { Value::I32(self) diff --git a/src/vm.rs b/src/vm.rs index 6f7a1d3..519831d 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -4,6 +4,7 @@ use crate::errors::{RuntimeError, ValueError}; use crate::value::Value; use std::collections::HashMap; use tracing::debug; +use crate::tokens::TokenType; macro_rules! define_var { ($self:ident, $variant:ident, $chunk:ident) => {{ @@ -40,7 +41,7 @@ pub fn interpret(registry: &HashMap, function: &str) -> Result, function: &str) -> Result { @@ -52,7 +53,7 @@ pub async fn interpret_async(registry: &HashMap, function: &str) error_occurred: false, registry, }; - vm.run(&chunk, vec![]) + vm.run(&chunk) } pub fn interpret_function(chunk: &Chunk, args: Vec) -> Result { @@ -63,14 +64,20 @@ pub fn interpret_function(chunk: &Chunk, args: Vec) -> Result Vm<'a> { - fn run(&mut self, chunk: &Chunk, args: Vec) -> Result { - for arg in args { - self.push(arg); + fn run_function(&mut self, chunk: &Chunk, mut args: Vec) -> Result { + // arguments -> locals + for (_,name) in chunk.vars.iter() { + self.local_vars.insert(name.clone(), args.remove(0)); } + self.run(&chunk) + } + + fn run(&mut self, chunk: &Chunk) -> Result { loop { if self.error_occurred { return Err(Something); @@ -131,64 +138,30 @@ impl<'a> Vm<'a> { let value = self.pop(); self.local_vars.insert(name, value); } - OP_DEF_I32 => { - let name = self.read_name(chunk); - let value = self.pop(); - let value = match value{ - Value::I32(v) => Value::I32(v), - Value::I64(v) => Value::I32(v as i32), - _ => unreachable!(), - }; - self.local_vars.insert(name, value); - } - OP_DEF_I64 => define_var!(self, I64, chunk), - OP_DEF_U32 => { - let name = self.read_name(chunk); - let value = self.pop(); - let value = match value{ - Value::U32(v) => Value::U32(v), - Value::I64(v) => Value::U32(v as u32), - _ => unreachable!(), - }; - self.local_vars.insert(name, value); - } - OP_DEF_U64 => { - let name = self.read_name(chunk); - let value = self.pop(); - let value = match value{ - Value::U64(v) => Value::U64(v), - Value::I64(v) => Value::U64(v as u64), - _ => unreachable!(), - }; - self.local_vars.insert(name, value); - } - OP_DEF_F32 => { - let name = self.read_name(chunk); - let value = self.pop(); - let value = match value{ - Value::F32(v) => Value::F32(v), - Value::F64(v) => Value::F32(v as f32), - _ => unreachable!(), - }; - self.local_vars.insert(name, value); - } - OP_DEF_F64 => define_var!(self, F64, chunk), - OP_DEF_STRING => define_var!(self, String, chunk), - OP_DEF_CHAR => define_var!(self, Char, chunk), - OP_DEF_BOOL => define_var!(self, Bool, chunk), - OP_DEF_DATE => define_var!(self, Date, chunk), OP_DEF_LIST => { - let name = self.read_name(chunk); let len = self.read(chunk); let mut list = vec![]; for _ in 0..len { let value = self.pop(); list.push(value); } - self.local_vars.insert(name, Value::List(list)); + list.reverse(); + self.push(Value::List(list)); + } + OP_ASSIGN=>{ + let index = self.read(chunk); + let (var_type, name) = chunk.vars.get(index).unwrap(); + let value = self.pop(); + let value = match var_type{ + TokenType::U32 => value.cast_u32()?, + TokenType::U64 => value.cast_u64()?, + TokenType::F32 => value.cast_f32()?, + TokenType::I32 => value.cast_i32()?, + _ => value, + }; + self.local_vars.insert(name.to_string(), value); } OP_DEF_MAP => { - let name = self.read_name(chunk); let len = self.read(chunk); let mut map = HashMap::new(); for _ in 0..len { @@ -196,12 +169,13 @@ impl<'a> Vm<'a> { let key = self.pop(); map.insert(key, value); } - self.local_vars.insert(name, Value::Map(map)); + self.push(Value::Map(map)); } OP_GET => { - let name = self.read_name(chunk); - let value = self.local_vars.get(&name).unwrap(); - self.push(value.clone()); // not happy + let var_index = self.read(chunk); + let (_,name_index)= chunk.vars.get(var_index).unwrap(); + let value = self.local_vars.get(name_index).unwrap(); + self.push(value.clone()); // not happy , take ownership, no clone debug!("after get {:?}", self.stack); } OP_CALL => { @@ -309,4 +283,4 @@ pub const OP_DEF_MAP: u16 = 37; pub const OP_DEF_STRUCT: u16 = 38; pub const OP_DEF_F32: u16 = 39; pub const OP_DEF_F64: u16 = 40; -// pub const OP_NEW_LIST: u16 = 40; +pub const OP_ASSIGN: u16 = 41;