fix reassignment bugs
This commit is contained in:
parent
44f063fb6b
commit
0bce2ae9eb
12 changed files with 226 additions and 63 deletions
|
|
@ -68,7 +68,6 @@ impl AstCompiler {
|
||||||
&mut self,
|
&mut self,
|
||||||
symbol_table: &mut SymbolTable,
|
symbol_table: &mut SymbolTable,
|
||||||
) -> Result<Vec<Statement>, CompilerErrorAtLine> {
|
) -> Result<Vec<Statement>, CompilerErrorAtLine> {
|
||||||
self.current_line();
|
|
||||||
if !self.had_error {
|
if !self.had_error {
|
||||||
let mut statements = vec![];
|
let mut statements = vec![];
|
||||||
while !self.is_at_end() {
|
while !self.is_at_end() {
|
||||||
|
|
@ -365,10 +364,19 @@ impl AstCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bit_xor(&mut self, symbol_table: &mut SymbolTable) -> Expr {
|
fn bit_xor(&mut self, symbol_table: &mut SymbolTable) -> Expr {
|
||||||
let expr = self.equality(symbol_table)?;
|
let expr = self.assignment(symbol_table)?;
|
||||||
self.binary(&[TokenType::BitXor], expr, symbol_table)
|
self.binary(&[TokenType::BitXor], expr, symbol_table)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn assignment(&mut self, symbol_table: &mut SymbolTable) -> Expr {
|
||||||
|
let expr = self.equality(symbol_table)?;
|
||||||
|
self.binary(
|
||||||
|
&[TokenType::Equal],
|
||||||
|
expr,
|
||||||
|
symbol_table,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn equality(&mut self, symbol_table: &mut SymbolTable) -> Expr {
|
fn equality(&mut self, symbol_table: &mut SymbolTable) -> Expr {
|
||||||
let expr = self.comparison(symbol_table)?;
|
let expr = self.comparison(symbol_table)?;
|
||||||
self.binary(
|
self.binary(
|
||||||
|
|
|
||||||
65
src/builtins/list.rs
Normal file
65
src/builtins/list.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
use crate::ast_compiler::Parameter;
|
||||||
|
use crate::builtins::{FunctionMap, Signature, add, expected};
|
||||||
|
use crate::errors::RuntimeError;
|
||||||
|
use crate::tokens::TokenType;
|
||||||
|
use crate::tokens::TokenType::U64;
|
||||||
|
use crate::value::{Value, u64};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
macro_rules! mut_list_fn {
|
||||||
|
(mut $list:ident, mut $args:ident => $body:expr) => {
|
||||||
|
|self_val: Value, mut $args: Vec<Value>| -> Result<Value, RuntimeError> {
|
||||||
|
match self_val {
|
||||||
|
Value::List(mut $list) => $body,
|
||||||
|
_ => Err(expected_a_list()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn list_functions() -> FunctionMap {
|
||||||
|
let mut list_functions: FunctionMap = HashMap::new();
|
||||||
|
let functions = &mut list_functions;
|
||||||
|
add(
|
||||||
|
functions,
|
||||||
|
"len",
|
||||||
|
Signature::new(
|
||||||
|
vec![],
|
||||||
|
U64,
|
||||||
|
mut_list_fn!(mut self_val, mut _args => Ok(u64(self_val.len() as u64))),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
add(
|
||||||
|
functions,
|
||||||
|
"push",
|
||||||
|
Signature::new(
|
||||||
|
vec![Parameter::new("element", TokenType::Any)],
|
||||||
|
U64,
|
||||||
|
mut_list_fn!(mut list, mut args => {
|
||||||
|
list.push(args.remove(0));
|
||||||
|
Ok(Value::List(list))
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
add(
|
||||||
|
functions,
|
||||||
|
"remove",
|
||||||
|
Signature::new(
|
||||||
|
vec![Parameter::new("index", U64)],
|
||||||
|
U64,
|
||||||
|
mut_list_fn!(mut list, mut args => {
|
||||||
|
let index = args.remove(0).cast_usize().unwrap();
|
||||||
|
if index >= list.len() {
|
||||||
|
return Err(RuntimeError::IndexOutOfBounds(index, list.len()))
|
||||||
|
}
|
||||||
|
list.remove(index);
|
||||||
|
Ok(Value::List(list))
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
list_functions
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expected_a_list() -> RuntimeError {
|
||||||
|
expected("list")
|
||||||
|
}
|
||||||
|
|
@ -1,24 +1,26 @@
|
||||||
mod string;
|
mod string;
|
||||||
|
mod list;
|
||||||
|
|
||||||
use crate::builtins::string::string_methods;
|
use crate::builtins::string::string_functions;
|
||||||
use crate::errors::{CompilerError, RuntimeError};
|
use crate::errors::{CompilerError, RuntimeError};
|
||||||
use crate::tokens::TokenType;
|
use crate::tokens::TokenType;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
use crate::ast_compiler::Parameter;
|
use crate::ast_compiler::Parameter;
|
||||||
|
use crate::builtins::list::list_functions;
|
||||||
|
|
||||||
pub(crate) struct Signature {
|
pub(crate) struct Signature {
|
||||||
pub(crate) parameters: Vec<Parameter>,
|
pub(crate) parameters: Vec<Parameter>,
|
||||||
pub(crate) return_type: TokenType,
|
pub(crate) return_type: TokenType,
|
||||||
pub(crate) function: MethodFn,
|
pub(crate) function: FunctionFn,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Signature {
|
impl Signature {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
parameters: Vec<Parameter>,
|
parameters: Vec<Parameter>,
|
||||||
return_type: TokenType,
|
return_type: TokenType,
|
||||||
function: MethodFn,
|
function: FunctionFn,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
parameters,
|
parameters,
|
||||||
|
|
@ -32,17 +34,19 @@ impl Signature {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) type MethodFn = fn(Value, Vec<Value>) -> Result<Value, RuntimeError>;
|
pub(crate) type FunctionFn = fn(Value, Vec<Value>) -> Result<Value, RuntimeError>;
|
||||||
pub(crate) type MethodMap = HashMap<String, Signature>;
|
pub(crate) type FunctionMap = HashMap<String, Signature>;
|
||||||
pub(crate) type MethodTable = HashMap<String, MethodMap>;
|
pub(crate) type FunctionTable = HashMap<String, FunctionMap>;
|
||||||
|
|
||||||
|
static METHODS: LazyLock<FunctionTable> = LazyLock::new(|| {
|
||||||
|
let mut table: FunctionTable = HashMap::new();
|
||||||
|
table.insert("string".to_string(), string_functions());
|
||||||
|
table.insert("list".to_string(), list_functions());
|
||||||
|
|
||||||
static METHODS: LazyLock<MethodTable> = LazyLock::new(|| {
|
|
||||||
let mut table: MethodTable = HashMap::new();
|
|
||||||
table.insert("string".to_string(), string_methods());
|
|
||||||
table
|
table
|
||||||
});
|
});
|
||||||
|
|
||||||
pub(crate) fn add(m: &mut MethodMap, name: &str, method: Signature) {
|
pub(crate) fn add(m: &mut FunctionMap, name: &str, method: Signature) {
|
||||||
m.insert(name.to_string(), method);
|
m.insert(name.to_string(), method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,35 +1,35 @@
|
||||||
use crate::builtins::{MethodMap, Parameter, Signature, add, expected};
|
use crate::builtins::{FunctionMap, Parameter, Signature, add, expected};
|
||||||
use crate::errors::RuntimeError;
|
use crate::errors::RuntimeError;
|
||||||
use crate::tokens::TokenType::{StringType, U64};
|
use crate::tokens::TokenType::{StringType, U64};
|
||||||
use crate::value::{Value, bool, i64, string};
|
use crate::value::{Value, bool, i64, string, u64};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub(crate) fn string_methods() -> MethodMap {
|
pub(crate) fn string_functions() -> FunctionMap {
|
||||||
let mut string_methods: MethodMap = HashMap::new();
|
let mut string_functions: FunctionMap = HashMap::new();
|
||||||
let m = &mut string_methods;
|
let functions = &mut string_functions;
|
||||||
add(m, "len", Signature::new(vec![], U64, string_len));
|
add(functions, "len", Signature::new(vec![], U64, string_len));
|
||||||
add(
|
add(
|
||||||
m,
|
functions,
|
||||||
"to_uppercase",
|
"to_uppercase",
|
||||||
Signature::new(vec![], StringType, string_to_uppercase),
|
Signature::new(vec![], StringType, string_to_uppercase),
|
||||||
);
|
);
|
||||||
add(
|
add(
|
||||||
m,
|
functions,
|
||||||
"to_lowercase",
|
"to_lowercase",
|
||||||
Signature::new(vec![], StringType, string_to_lowercase),
|
Signature::new(vec![], StringType, string_to_lowercase),
|
||||||
);
|
);
|
||||||
add(m, "contains", Signature::new(vec![], StringType, string_contains));
|
add(functions, "contains", Signature::new(vec![Parameter::new("key", StringType)], StringType, string_contains));
|
||||||
add(m, "reverse", Signature::new(vec![], StringType, string_reverse));
|
add(functions, "reverse", Signature::new(vec![], StringType, string_reverse));
|
||||||
add(m, "trim", Signature::new(vec![], StringType, string_trim));
|
add(functions, "trim", Signature::new(vec![], StringType, string_trim));
|
||||||
add(
|
add(
|
||||||
m,
|
functions,
|
||||||
"trim_start",
|
"trim_start",
|
||||||
Signature::new(vec![], StringType, string_trim_start),
|
Signature::new(vec![], StringType, string_trim_start),
|
||||||
);
|
);
|
||||||
add(m, "trim_end", Signature::new(vec![], StringType, string_trim_end));
|
add(functions, "trim_end", Signature::new(vec![], StringType, string_trim_end));
|
||||||
add(
|
add(
|
||||||
m,
|
functions,
|
||||||
"replace_all",
|
"replace_all",
|
||||||
Signature::new(
|
Signature::new(
|
||||||
vec![
|
vec![
|
||||||
|
|
@ -40,12 +40,12 @@ pub(crate) fn string_methods() -> MethodMap {
|
||||||
string_replace_all,
|
string_replace_all,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
string_methods
|
string_functions
|
||||||
}
|
}
|
||||||
|
|
||||||
fn string_len(self_val: Value, _args: Vec<Value>) -> Result<Value, RuntimeError> {
|
fn string_len(self_val: Value, _args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||||
match self_val {
|
match self_val {
|
||||||
Value::String(s) => Ok(i64(s.len() as i64)),
|
Value::String(s) => Ok(u64(s.len() as u64)),
|
||||||
_ => Err(expected_a_string()),
|
_ => Err(expected_a_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -98,7 +98,6 @@ fn string_trim_end(self_val: Value, _: Vec<Value>) -> Result<Value, RuntimeError
|
||||||
_ => Err(expected_a_string()),
|
_ => Err(expected_a_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//TODO check arity in compiler (generically)
|
|
||||||
fn string_replace_all(receiver: Value, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
fn string_replace_all(receiver: Value, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||||
let pattern = if let Value::String(s) = &args[0] {
|
let pattern = if let Value::String(s) = &args[0] {
|
||||||
Regex::new(s).map_err(|_| RuntimeError::IllegalArgumentException("Invalid regex".into()))?
|
Regex::new(s).map_err(|_| RuntimeError::IllegalArgumentException("Invalid regex".into()))?
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use crate::ast_compiler::{Expression, Function, Parameter, Statement};
|
||||||
use crate::builtins::lookup;
|
use crate::builtins::lookup;
|
||||||
use crate::chunk::Chunk;
|
use crate::chunk::Chunk;
|
||||||
use crate::errors::CompilerError::{IncompatibleTypes, UndeclaredVariable};
|
use crate::errors::CompilerError::{IncompatibleTypes, UndeclaredVariable};
|
||||||
|
use crate::errors::RuntimeError::IllegalArgumentsException;
|
||||||
use crate::errors::{CompilerError, CompilerErrorAtLine};
|
use crate::errors::{CompilerError, CompilerErrorAtLine};
|
||||||
use crate::symbol_builder::{Symbol, calculate_type, infer_type};
|
use crate::symbol_builder::{Symbol, calculate_type, infer_type};
|
||||||
use crate::tokens::TokenType;
|
use crate::tokens::TokenType;
|
||||||
|
|
@ -12,11 +13,12 @@ use crate::vm::{
|
||||||
OP_ADD, OP_AND, OP_ASSIGN, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CALL_BUILTIN,
|
OP_ADD, OP_AND, OP_ASSIGN, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CALL_BUILTIN,
|
||||||
OP_CONSTANT, OP_DEF_LIST, OP_DEF_MAP, OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER,
|
OP_CONSTANT, OP_DEF_LIST, OP_DEF_MAP, OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER,
|
||||||
OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_LIST_GET, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_OR,
|
OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_LIST_GET, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_OR,
|
||||||
OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
|
OP_POP, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
|
||||||
};
|
};
|
||||||
use crate::{Registry, SymbolTable};
|
use crate::{Registry, SymbolTable};
|
||||||
|
use clap::arg;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::mem;
|
use std::ops::Deref;
|
||||||
|
|
||||||
pub fn compile(
|
pub fn compile(
|
||||||
qualified_name: Option<&str>,
|
qualified_name: Option<&str>,
|
||||||
|
|
@ -216,7 +218,13 @@ impl Compiler {
|
||||||
.add_constant(Value::String(method_name.to_string()))
|
.add_constant(Value::String(method_name.to_string()))
|
||||||
});
|
});
|
||||||
let signature = lookup(&receiver_type, method_name).map_err(|e| self.raise(e))?;
|
let signature = lookup(&receiver_type, method_name).map_err(|e| self.raise(e))?;
|
||||||
|
if signature.parameters.len() != arguments.len() {
|
||||||
|
return Err(self.raise(CompilerError::IllegalArgumentsException(
|
||||||
|
format!("{}.{}", receiver_type, method_name),
|
||||||
|
signature.parameters.len(),
|
||||||
|
arguments.len(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
self.get_arguments_in_order(
|
self.get_arguments_in_order(
|
||||||
namespace,
|
namespace,
|
||||||
symbols,
|
symbols,
|
||||||
|
|
@ -279,22 +287,31 @@ impl Compiler {
|
||||||
self.compile_expression(namespace, left, symbols, registry)?;
|
self.compile_expression(namespace, left, symbols, registry)?;
|
||||||
self.compile_expression(namespace, right, symbols, registry)?;
|
self.compile_expression(namespace, right, symbols, registry)?;
|
||||||
match operator.token_type {
|
match operator.token_type {
|
||||||
TokenType::Plus => self.emit_byte(OP_ADD),
|
|
||||||
TokenType::Minus => self.emit_byte(OP_SUBTRACT),
|
|
||||||
TokenType::Star => self.emit_byte(OP_MULTIPLY),
|
|
||||||
TokenType::Slash => self.emit_byte(OP_DIVIDE),
|
|
||||||
TokenType::BitAnd => self.emit_byte(OP_BITAND),
|
TokenType::BitAnd => self.emit_byte(OP_BITAND),
|
||||||
TokenType::Pipe => self.emit_byte(OP_BITOR),
|
|
||||||
TokenType::BitXor => self.emit_byte(OP_BITXOR),
|
TokenType::BitXor => self.emit_byte(OP_BITXOR),
|
||||||
TokenType::GreaterGreater => self.emit_byte(OP_SHR),
|
TokenType::Equal => {
|
||||||
TokenType::LessLess => self.emit_byte(OP_SHL),
|
if let Expression::Variable { name, .. } = left.deref() {
|
||||||
|
let index = self.vars.get(name).unwrap();
|
||||||
|
self.emit_bytes(OP_ASSIGN, *index as u16);
|
||||||
|
self.emit_byte(OP_POP);
|
||||||
|
} else {
|
||||||
|
return Err(self.raise(UndeclaredVariable("".to_string())));
|
||||||
|
}
|
||||||
|
}
|
||||||
TokenType::EqualEqual => self.emit_byte(OP_EQUAL),
|
TokenType::EqualEqual => self.emit_byte(OP_EQUAL),
|
||||||
TokenType::Greater => self.emit_byte(OP_GREATER),
|
TokenType::Greater => self.emit_byte(OP_GREATER),
|
||||||
TokenType::GreaterEqual => self.emit_byte(OP_GREATER_EQUAL),
|
TokenType::GreaterEqual => self.emit_byte(OP_GREATER_EQUAL),
|
||||||
|
TokenType::GreaterGreater => self.emit_byte(OP_SHR),
|
||||||
TokenType::Less => self.emit_byte(OP_LESS),
|
TokenType::Less => self.emit_byte(OP_LESS),
|
||||||
TokenType::LessEqual => self.emit_byte(OP_LESS_EQUAL),
|
TokenType::LessEqual => self.emit_byte(OP_LESS_EQUAL),
|
||||||
|
TokenType::LessLess => self.emit_byte(OP_SHL),
|
||||||
TokenType::LogicalAnd => self.emit_byte(OP_AND),
|
TokenType::LogicalAnd => self.emit_byte(OP_AND),
|
||||||
TokenType::LogicalOr => self.emit_byte(OP_OR),
|
TokenType::LogicalOr => self.emit_byte(OP_OR),
|
||||||
|
TokenType::Minus => self.emit_byte(OP_SUBTRACT),
|
||||||
|
TokenType::Pipe => self.emit_byte(OP_BITOR),
|
||||||
|
TokenType::Plus => self.emit_byte(OP_ADD),
|
||||||
|
TokenType::Slash => self.emit_byte(OP_DIVIDE),
|
||||||
|
TokenType::Star => self.emit_byte(OP_MULTIPLY),
|
||||||
_ => unimplemented!("binary other than plus, minus, star, slash"),
|
_ => unimplemented!("binary other than plus, minus, star, slash"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::errors::CompilerError::IllegalArgumentsException;
|
||||||
|
use crate::errors::CompilerErrorAtLine;
|
||||||
|
use crate::errors::CrudLangError::{Compiler, Runtime};
|
||||||
use crate::value::{Value, string};
|
use crate::value::{Value, string};
|
||||||
use crate::{compile, run};
|
use crate::{compile, run};
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
|
use crate::errors::RuntimeError::{IllegalArgumentException, IndexOutOfBounds};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn literal_int() {
|
fn literal_int() {
|
||||||
|
|
@ -165,14 +169,8 @@ p"#);
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
let result = result.unwrap();
|
let result = result.unwrap();
|
||||||
if let Value::Map(map) = result {
|
if let Value::Map(map) = result {
|
||||||
assert_eq!(
|
assert_eq!(map.get(&string("name")).unwrap(), &string("Dent"));
|
||||||
map.get(&string("name")).unwrap(),
|
assert_eq!(map.get(&string("age")).unwrap(), &Value::I64(40));
|
||||||
&string("Dent")
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
map.get(&string("age")).unwrap(),
|
|
||||||
&Value::I64(40)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -183,10 +181,7 @@ m"#);
|
||||||
|
|
||||||
let result = result.unwrap();
|
let result = result.unwrap();
|
||||||
if let Value::Map(map) = result {
|
if let Value::Map(map) = result {
|
||||||
assert_eq!(
|
assert_eq!(map.get(&string("name")).unwrap(), &string("Dent"));
|
||||||
map.get(&string("name")).unwrap(),
|
|
||||||
&string("Dent")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -267,7 +262,7 @@ date"#),
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn string_len() {
|
fn string_len() {
|
||||||
assert_eq!(run(r#""abc".len()"#), Ok(Value::I64(3)));
|
assert_eq!(run(r#""abc".len()"#), Ok(Value::U64(3)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -275,6 +270,56 @@ date"#),
|
||||||
assert_eq!(run(r#""Hello".replace_all("l","p")"#), Ok(string("Heppo")));
|
assert_eq!(run(r#""Hello".replace_all("l","p")"#), Ok(string("Heppo")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_replace_wrong_nr_of_args() {
|
||||||
|
assert_eq!(
|
||||||
|
run(r#""Hello".replace_all("l")"#),
|
||||||
|
Err(Compiler(CompilerErrorAtLine {
|
||||||
|
error: IllegalArgumentsException("string.replace_all".to_string(), 2, 1),
|
||||||
|
line: 1
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_replace_wrong_type_of_args() {
|
||||||
|
assert_eq!(
|
||||||
|
run(r#""Hello".replace_all("l", 1)"#),
|
||||||
|
Err(Runtime(IllegalArgumentException("Illegal replacement. Expected a string but got 1".to_string())))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn string_contains() {
|
||||||
|
assert_eq!(run(r#""Hello".contains("l")"#), Ok(Value::Bool(true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_length(){
|
||||||
|
assert_eq!(run(r#"[1,2,3].len()"#), Ok(Value::U64(3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_push(){
|
||||||
|
assert_eq!(run(r#"[1,2].push(3)"#), Ok(Value::List(vec![Value::I64(1), Value::I64(2), Value::I64(3)])));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_remove(){
|
||||||
|
assert_eq!(run(r#"[1,2,3].remove(0)"#), Ok(Value::List(vec![Value::I64(2), Value::I64(3)])));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_remove_out_of_bounds(){
|
||||||
|
assert_eq!(run(r#"[1,2,3].remove(4)"#), Err(Runtime(IndexOutOfBounds(4, 3))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn reassign(){
|
||||||
|
assert_eq!(run(r#"let a=1
|
||||||
|
a=2"#), Ok(Value::Void));
|
||||||
|
}
|
||||||
|
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn package() {
|
// fn package() {
|
||||||
// assert_eq!(run(r#"a.b.c()"#), Ok(Value::U32(48)));
|
// assert_eq!(run(r#"a.b.c()"#), Ok(Value::U32(48)));
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,8 @@ pub enum CompilerError {
|
||||||
IllegalIndexArgument(TokenType),
|
IllegalIndexArgument(TokenType),
|
||||||
#[error("Illegal argument: '{0}' cannot be indexed")]
|
#[error("Illegal argument: '{0}' cannot be indexed")]
|
||||||
IllegalTypeToIndex(String),
|
IllegalTypeToIndex(String),
|
||||||
|
#[error("The number of of arguments for {0} is not correct. Should be {1}, got {2}")]
|
||||||
|
IllegalArgumentsException(String,usize,usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
#[derive(Error, Debug, PartialEq)]
|
||||||
|
|
@ -87,6 +89,8 @@ pub enum RuntimeError {
|
||||||
IllegalArgumentException(String),
|
IllegalArgumentException(String),
|
||||||
#[error("Expected {0}")]
|
#[error("Expected {0}")]
|
||||||
ExpectedType(String),
|
ExpectedType(String),
|
||||||
|
#[error("Index out of bounds: {0} > {1}")]
|
||||||
|
IndexOutOfBounds(usize, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
#[derive(Error, Debug, PartialEq)]
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ async fn main() -> Result<(), CrudLangError> {
|
||||||
);
|
);
|
||||||
|
|
||||||
if args.repl {
|
if args.repl {
|
||||||
std::thread::spawn(move || tipi_lang::repl::start(swap.clone()).unwrap());
|
let _ = std::thread::spawn(move || tipi_lang::repl::start(swap.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
axum::serve(listener, app).await.map_err(map_underlying())?;
|
axum::serve(listener, app).await.map_err(map_underlying())?;
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,13 @@ pub fn start(registry: Arc<ArcSwap<HashMap<String, Chunk>>>) -> Result<(), CrudL
|
||||||
|
|
||||||
let tokens = scan(input)?;
|
let tokens = scan(input)?;
|
||||||
|
|
||||||
let ast = ast_compiler::compile(None, tokens, &mut symbol_table)?;
|
let ast = match ast_compiler::compile(None, tokens, &mut symbol_table){
|
||||||
|
Ok(ast) => ast,
|
||||||
|
Err(e) => {
|
||||||
|
println!("{}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
symbol_builder::build("", &ast, &mut symbol_table);
|
symbol_builder::build("", &ast, &mut symbol_table);
|
||||||
|
|
||||||
match bytecode_compiler.compile(&ast, &symbol_table, &mut registry_copy, "") {
|
match bytecode_compiler.compile(&ast, &symbol_table, &mut registry_copy, "") {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ impl Token {
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Hash)]
|
#[derive(Debug, PartialEq, Clone, Hash)]
|
||||||
pub enum TokenType {
|
pub enum TokenType {
|
||||||
|
Any,
|
||||||
Bang,
|
Bang,
|
||||||
BangEqual,
|
BangEqual,
|
||||||
BitAnd,
|
BitAnd,
|
||||||
|
|
@ -93,6 +94,7 @@ pub enum TokenType {
|
||||||
impl fmt::Display for TokenType {
|
impl fmt::Display for TokenType {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
TokenType::Any => write!(f, "any"),
|
||||||
TokenType::StringType => write!(f, "string"),
|
TokenType::StringType => write!(f, "string"),
|
||||||
TokenType::DateTime => write!(f, "datetime"),
|
TokenType::DateTime => write!(f, "datetime"),
|
||||||
TokenType::Char => write!(f, "char"),
|
TokenType::Char => write!(f, "char"),
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,10 @@ pub(crate) fn i64(v: impl Into<i64>) -> Value {
|
||||||
Value::I64(v.into())
|
Value::I64(v.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn u64(v: impl Into<u64>) -> Value {
|
||||||
|
Value::U64(v.into())
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn bool(v: impl Into<bool>) -> Value {
|
pub(crate) fn bool(v: impl Into<bool>) -> Value {
|
||||||
Value::Bool(v.into())
|
Value::Bool(v.into())
|
||||||
}
|
}
|
||||||
|
|
@ -419,6 +423,7 @@ impl PartialEq for Value {
|
||||||
equal
|
equal
|
||||||
}
|
}
|
||||||
// TODO objects
|
// TODO objects
|
||||||
|
(Value::Void, Value::Void) => true,
|
||||||
_ => false, //?
|
_ => false, //?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
26
src/vm.rs
26
src/vm.rs
|
|
@ -156,13 +156,7 @@ impl Vm {
|
||||||
let index = self.read(chunk);
|
let index = self.read(chunk);
|
||||||
let (var_type, name) = chunk.vars.get(index).unwrap();
|
let (var_type, name) = chunk.vars.get(index).unwrap();
|
||||||
let value = self.pop();
|
let value = self.pop();
|
||||||
let value = match var_type {
|
let value = Self::number(var_type, value)?;
|
||||||
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);
|
self.local_vars.insert(name.to_string(), value);
|
||||||
}
|
}
|
||||||
OP_DEF_MAP => {
|
OP_DEF_MAP => {
|
||||||
|
|
@ -178,8 +172,8 @@ impl Vm {
|
||||||
OP_GET => {
|
OP_GET => {
|
||||||
let var_index = self.read(chunk);
|
let var_index = self.read(chunk);
|
||||||
let (_, name_index) = chunk.vars.get(var_index).unwrap();
|
let (_, name_index) = chunk.vars.get(var_index).unwrap();
|
||||||
let value = self.local_vars.get(name_index).unwrap();
|
let value = self.local_vars.remove(name_index).unwrap();
|
||||||
self.push(value.clone()); // not happy , take ownership, no clone
|
self.push(value);
|
||||||
}
|
}
|
||||||
OP_LIST_GET => {
|
OP_LIST_GET => {
|
||||||
let index = self.pop();
|
let index = self.pop();
|
||||||
|
|
@ -205,6 +199,9 @@ impl Vm {
|
||||||
let return_value = crate::builtins::call(&receiver_type_name, &function_name, receiver, args)?;
|
let return_value = crate::builtins::call(&receiver_type_name, &function_name, receiver, args)?;
|
||||||
self.push(return_value);
|
self.push(return_value);
|
||||||
}
|
}
|
||||||
|
OP_POP =>{
|
||||||
|
self.pop(); // discards the value
|
||||||
|
}
|
||||||
OP_CALL => {
|
OP_CALL => {
|
||||||
let function_name_index = self.read(chunk);
|
let function_name_index = self.read(chunk);
|
||||||
let num_args = self.read(chunk);
|
let num_args = self.read(chunk);
|
||||||
|
|
@ -259,6 +256,17 @@ impl Vm {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn number(var_type: &TokenType, value: Value) -> Result<Value, RuntimeError> {
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
fn read(&mut self, chunk: &Chunk) -> usize {
|
fn read(&mut self, chunk: &Chunk) -> usize {
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
chunk.code[self.ip - 1] as usize
|
chunk.code[self.ip - 1] as usize
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue