added more builtin functions
This commit is contained in:
parent
e5b03d9037
commit
8eeb09855f
12 changed files with 148 additions and 23 deletions
|
|
@ -130,8 +130,9 @@ fn get() -> [Customer] | Customer? | ():
|
|||
* test support
|
||||
|
||||
## What about performance?
|
||||
* Clueless really! We'll see.
|
||||
* But it is written in rust
|
||||
* Not optimizing for performance yet. First results look pretty bad.
|
||||
* as in: purely recursive fibonacci of 38 (the 38th fibonacci number) takes 42 seconds on my machine (in release mode) :sad-face.
|
||||
* That said the idea is that a lot of processing is done within the virtual machine.
|
||||
* And it has no GC
|
||||
* So, maybe it will compete with python?
|
||||
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@ fn fib(n: u64):
|
|||
else:
|
||||
fib(n-1) + fib(n-2)
|
||||
|
||||
println(fib(10))
|
||||
println("fib = " + fib(38))
|
||||
|
|
@ -22,6 +22,11 @@ pub(crate) fn list_functions() -> FunctionMap {
|
|||
"remove",
|
||||
Signature::new(vec![Parameter::new("index", U64)], U64, remove),
|
||||
);
|
||||
add(
|
||||
functions,
|
||||
"sort",
|
||||
Signature::new(vec![], U64, sort),
|
||||
);
|
||||
list_functions
|
||||
}
|
||||
|
||||
|
|
@ -55,6 +60,19 @@ fn len(self_val: RefMut<Value>, _args: Vec<Value>) -> Result<Value, RuntimeError
|
|||
}
|
||||
}
|
||||
|
||||
fn sort(mut self_val: RefMut<Value>, _args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if let Value::List(list) = self_val.deref_mut() {
|
||||
if list.windows(2).any(|w| w[0].sort_cmp(&w[1]).is_none()) {
|
||||
return Err(RuntimeError::NotSortable);
|
||||
}
|
||||
|
||||
list.sort_by(|a, b| a.sort_cmp(b).unwrap());
|
||||
Ok(Value::Void)
|
||||
} else {
|
||||
Err(expected_a_list())
|
||||
}
|
||||
}
|
||||
|
||||
fn expected_a_list() -> RuntimeError {
|
||||
expected("list")
|
||||
}
|
||||
|
|
|
|||
64
src/builtins/map.rs
Normal file
64
src/builtins/map.rs
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
use crate::builtins::{FunctionMap, Signature, add, expected};
|
||||
use crate::compiler::ast_pass::Parameter;
|
||||
use crate::compiler::tokens::TokenType;
|
||||
use crate::compiler::tokens::TokenType::{U64, Void};
|
||||
use crate::errors::RuntimeError;
|
||||
use crate::value::{Value, u64};
|
||||
use std::cell::RefMut;
|
||||
use std::collections::HashMap;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
pub(crate) fn map_functions() -> FunctionMap {
|
||||
let mut map_functions: FunctionMap = HashMap::new();
|
||||
let functions = &mut map_functions;
|
||||
add(functions, "len", Signature::new(vec![], U64, len));
|
||||
add(
|
||||
functions,
|
||||
"insert",
|
||||
Signature::new(
|
||||
vec![
|
||||
Parameter::new("key", TokenType::Any),
|
||||
Parameter::new("value", TokenType::Any),
|
||||
],
|
||||
Void,
|
||||
insert,
|
||||
),
|
||||
);
|
||||
add(
|
||||
functions,
|
||||
"remove",
|
||||
Signature::new(vec![Parameter::new("key", TokenType::Any)], Void, remove),
|
||||
);
|
||||
map_functions
|
||||
}
|
||||
|
||||
fn remove(mut self_val: RefMut<Value>, mut args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if let Value::Map(map) = self_val.deref_mut() {
|
||||
let key = args.remove(0);
|
||||
map.remove(&key);
|
||||
Ok(Value::Void)
|
||||
} else {
|
||||
Err(expected_a_map())
|
||||
}
|
||||
}
|
||||
|
||||
fn insert(mut self_val: RefMut<Value>, mut args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if let Value::Map(map) = self_val.deref_mut() {
|
||||
map.insert(args.remove(0),args.remove(0));
|
||||
Ok(Value::Void)
|
||||
} else {
|
||||
Err(expected_a_map())
|
||||
}
|
||||
}
|
||||
|
||||
fn len(self_val: RefMut<Value>, _args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
if let Value::Map(map) = self_val.deref() {
|
||||
Ok(u64(map.len() as u64))
|
||||
} else {
|
||||
Err(expected_a_map())
|
||||
}
|
||||
}
|
||||
|
||||
fn expected_a_map() -> RuntimeError {
|
||||
expected("map")
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
mod string;
|
||||
mod list;
|
||||
pub(crate) mod globals;
|
||||
mod map;
|
||||
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use crate::builtins::string::string_functions;
|
||||
|
|
@ -12,6 +13,7 @@ use std::rc::Rc;
|
|||
use std::sync::LazyLock;
|
||||
use crate::compiler::ast_pass::Parameter;
|
||||
use crate::builtins::list::list_functions;
|
||||
use crate::builtins::map::map_functions;
|
||||
|
||||
pub(crate) struct Signature {
|
||||
pub(crate) parameters: Vec<Parameter>,
|
||||
|
|
@ -32,8 +34,8 @@ impl Signature {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn arity(&self) -> usize {
|
||||
self.parameters.len()
|
||||
pub(crate) fn arity(&self) -> u8 {
|
||||
self.parameters.len() as u8
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -47,6 +49,7 @@ static FUNCTIONS: LazyLock<FunctionTable> = LazyLock::new(|| {
|
|||
let mut table: FunctionTable = HashMap::new();
|
||||
table.insert("string".to_string(), string_functions());
|
||||
table.insert("list".to_string(), list_functions());
|
||||
table.insert("map".to_string(), map_functions());
|
||||
|
||||
table
|
||||
});
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ impl AsmPass {
|
|||
namespace, symbols, registry, arguments, parameters,
|
||||
)?;
|
||||
|
||||
self.emit(Call(name_index, arguments.len()));
|
||||
self.emit(Call(name_index, arguments.len() as u8));
|
||||
}
|
||||
// constructor function
|
||||
Some(Symbol::Object { fields, .. }) => {
|
||||
|
|
@ -280,7 +280,7 @@ impl AsmPass {
|
|||
self.get_arguments_in_order(
|
||||
namespace, symbols, registry, arguments, fields,
|
||||
)?;
|
||||
self.emit(Call(name_index, arguments.len()));
|
||||
self.emit(Call(name_index, arguments.len() as u8));
|
||||
}
|
||||
// maybe global function
|
||||
_ => {
|
||||
|
|
@ -325,7 +325,7 @@ impl AsmPass {
|
|||
});
|
||||
let signature =
|
||||
lookup(&receiver_type, method_name).map_err(|e| self.error_at_line(e))?;
|
||||
if signature.arity() != arguments.len() {
|
||||
if signature.arity() != arguments.len() as u8 {
|
||||
return Err(self.error_at_line(CompilerError::IllegalArgumentsException(
|
||||
format!("{}.{}", receiver_type, method_name),
|
||||
signature.parameters.len(),
|
||||
|
|
@ -339,7 +339,7 @@ impl AsmPass {
|
|||
arguments,
|
||||
&signature.parameters,
|
||||
)?;
|
||||
self.emit(CallBuiltin(name_index, type_index, arguments.len()));
|
||||
self.emit(CallBuiltin(name_index, type_index, arguments.len() as u8));
|
||||
}
|
||||
Expression::Variable { name, .. } => {
|
||||
let name_index = self.vars.get(name);
|
||||
|
|
@ -444,11 +444,7 @@ impl AsmPass {
|
|||
}
|
||||
Expression::MapGet { .. } => {}
|
||||
Expression::FieldGet { .. } => {}
|
||||
Expression::Range { lower, upper, .. } => {
|
||||
// opposite order, because we have to assign last one first to the loop variable
|
||||
// self.compile_expression(namespace, upper, symbols, registry)?;
|
||||
// self.compile_expression(namespace, lower, symbols, registry)?;
|
||||
}
|
||||
Expression::Range { lower, .. } => {}
|
||||
Expression::ForStatement {
|
||||
loop_var,
|
||||
range,
|
||||
|
|
@ -550,7 +546,7 @@ pub enum Op {
|
|||
Divide,
|
||||
Negate,
|
||||
Return,
|
||||
Call(usize, usize),
|
||||
Call(usize, u8),
|
||||
And,
|
||||
Or,
|
||||
Not,
|
||||
|
|
@ -571,7 +567,7 @@ pub enum Op {
|
|||
DefMap(usize),
|
||||
Assign(usize),
|
||||
ListGet,
|
||||
CallBuiltin(usize, usize, usize),
|
||||
CallBuiltin(usize, usize, u8),
|
||||
Dup,
|
||||
GotoIf(usize),
|
||||
GotoIfNot(usize),
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ mod tests {
|
|||
use crate::compiler::{compile, run};
|
||||
use crate::errors::CompilerError::{IllegalArgumentsException, ReservedFunctionName};
|
||||
use crate::errors::CompilerErrorAtLine;
|
||||
use crate::errors::RuntimeError::{IllegalArgumentException, IndexOutOfBounds};
|
||||
use crate::errors::RuntimeError::{IllegalArgumentException, IndexOutOfBounds, NotSortable};
|
||||
use crate::errors::TipiLangError::{Compiler, Runtime};
|
||||
use crate::value::{Value, string};
|
||||
use crate::value::{Value, string, i64};
|
||||
use chrono::DateTime;
|
||||
|
||||
#[test]
|
||||
|
|
@ -299,6 +299,28 @@ date"#),
|
|||
assert_eq!(run(r#"[1,2,3].len()"#), Ok(Value::U64(3)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_sort() {
|
||||
assert_eq!(
|
||||
run(r#"
|
||||
let a = [3,2,1]
|
||||
a.sort()
|
||||
a"#),
|
||||
Ok(Value::List(vec![i64(1), i64(2), i64(3)]))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_sort_invalid() {
|
||||
assert_eq!(
|
||||
run(r#"
|
||||
let a = [3,2,"a"]
|
||||
a.sort()
|
||||
a"#),
|
||||
Err(Runtime(NotSortable))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_push() {
|
||||
assert_eq!(
|
||||
|
|
@ -449,7 +471,8 @@ let a:i64 = if true:
|
|||
42
|
||||
else:
|
||||
0
|
||||
a"#).unwrap();
|
||||
a"#)
|
||||
.unwrap();
|
||||
}
|
||||
// #[test]
|
||||
// fn package() {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ pub fn compile_sourcedir(source_dir: &str) -> Result<HashMap<String, AsmChunk>,
|
|||
for entry in WalkDir::new(source_dir).into_iter().filter_map(|e| e.ok()) {
|
||||
let path = entry.path().to_str().unwrap();
|
||||
if path.ends_with(TIPI_EXT) {
|
||||
print!("-- Compiling {} -- ", path);
|
||||
println!("-- Compiling {} -- ", path);
|
||||
let source = fs::read_to_string(path).map_err(map_underlying())?;
|
||||
let tokens = scan_pass::scan(&source)?;
|
||||
let mut symbol_table = HashMap::new();
|
||||
|
|
|
|||
|
|
@ -96,6 +96,8 @@ pub enum RuntimeError {
|
|||
ExpectedType(String),
|
||||
#[error("Index out of bounds: {0} > {1}")]
|
||||
IndexOutOfBounds(usize, usize),
|
||||
#[error("The list contains values that are not comparable.")]
|
||||
NotSortable,
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ pub mod file_watch;
|
|||
mod keywords;
|
||||
pub mod repl;
|
||||
mod symbol_builder;
|
||||
mod value;
|
||||
pub mod value;
|
||||
pub mod vm;
|
||||
|
||||
pub(crate) type SymbolTable = HashMap<String, Symbol>;
|
||||
|
|
|
|||
20
src/value.rs
20
src/value.rs
|
|
@ -37,7 +37,7 @@ pub(crate) fn string(v: impl Into<String>) -> Value {
|
|||
Value::String(v.into())
|
||||
}
|
||||
|
||||
pub(crate) fn _i64(v: impl Into<i64>) -> Value {
|
||||
pub(crate) fn i64(v: impl Into<i64>) -> Value {
|
||||
Value::I64(v.into())
|
||||
}
|
||||
|
||||
|
|
@ -109,6 +109,24 @@ impl Value {
|
|||
_ => Err(ValueError::IllegalCast),
|
||||
}
|
||||
}
|
||||
|
||||
/// Comparison used for sorting lists.
|
||||
/// Returns `None` when values are not comparable (different variants, unsupported types, etc.).
|
||||
pub fn sort_cmp(&self, rhs: &Self) -> Option<Ordering> {
|
||||
match (self, rhs) {
|
||||
(Value::I32(a), Value::I32(b)) => Some(a.cmp(b)),
|
||||
(Value::I64(a), Value::I64(b)) => Some(a.cmp(b)),
|
||||
(Value::U32(a), Value::U32(b)) => Some(a.cmp(b)),
|
||||
(Value::U64(a), Value::U64(b)) => Some(a.cmp(b)),
|
||||
(Value::F32(a), Value::F32(b)) => Some(a.total_cmp(b)),
|
||||
(Value::F64(a), Value::F64(b)) => Some(a.total_cmp(b)),
|
||||
(Value::String(a), Value::String(b)) => Some(a.cmp(b)),
|
||||
(Value::Char(a), Value::Char(b)) => Some(a.cmp(b)),
|
||||
(Value::Bool(a), Value::Bool(b)) => Some(a.cmp(b)),
|
||||
(Value::DateTime(a), Value::DateTime(b)) => Some(a.cmp(b)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for Value {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue