Compare commits
No commits in common. "c4dd82d191ede60857382a5ea06f81b00d6fdd23" and "e5b03d9037013b95efedad71d6c5fe3f5a4e9d9f" have entirely different histories.
c4dd82d191
...
e5b03d9037
31 changed files with 164 additions and 552 deletions
81
Cargo.lock
generated
81
Cargo.lock
generated
|
|
@ -163,29 +163,6 @@ version = "0.22.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "bb8"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "457d7ed3f888dfd2c7af56d4975cade43c622f74bdcddfed6d4352f57acc6310"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"parking_lot",
|
||||
"portable-atomic",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bb8-postgres"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e570e6557cd0f88d28d32afa76644873271a70dc22656df565b2021c4036aa9c"
|
||||
dependencies = [
|
||||
"bb8",
|
||||
"tokio",
|
||||
"tokio-postgres",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
|
@ -250,7 +227,6 @@ dependencies = [
|
|||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
|
@ -328,9 +304,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.7"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
|
|
@ -516,9 +492,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
version = "0.14.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
|
|
@ -959,13 +935,13 @@ checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
|||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.12"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
|
||||
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
"libc",
|
||||
"redox_syscall 0.7.0",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -1231,7 +1207,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall 0.5.18",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
|
@ -1279,26 +1255,6 @@ version = "0.3.32"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f89776e4d69bb58bc6993e99ffa1d11f228b839984854c7daeb5d37f87cbe950"
|
||||
|
||||
[[package]]
|
||||
name = "postgres"
|
||||
version = "0.19.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7c48ece1c6cda0db61b058c1721378da76855140e9214339fa1317decacb176"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fallible-iterator",
|
||||
"futures-util",
|
||||
"log",
|
||||
"tokio",
|
||||
"tokio-postgres",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "postgres-protocol"
|
||||
version = "0.6.9"
|
||||
|
|
@ -1324,7 +1280,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ef4605b7c057056dd35baeb6ac0c0338e4975b1f2bef0f65da953285eb007095"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"chrono",
|
||||
"fallible-iterator",
|
||||
"postgres-protocol",
|
||||
]
|
||||
|
|
@ -1409,15 +1364,6 @@ dependencies = [
|
|||
"bitflags 2.9.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27"
|
||||
dependencies = [
|
||||
"bitflags 2.9.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.2"
|
||||
|
|
@ -1924,15 +1870,12 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"arc-swap",
|
||||
"axum",
|
||||
"bb8",
|
||||
"bb8-postgres",
|
||||
"chrono",
|
||||
"clap",
|
||||
"dotenv",
|
||||
"log",
|
||||
"log4rs",
|
||||
"notify",
|
||||
"postgres",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"serde",
|
||||
|
|
@ -2204,18 +2147,18 @@ checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
|
|||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.25"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8"
|
||||
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-properties"
|
||||
version = "0.1.4"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d"
|
||||
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ log4rs = "1.4.0"
|
|||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
tokio = { version = "1.47", features = ["full"] }
|
||||
tokio-postgres = "0.7"
|
||||
chrono = { version= "0.4", features = ["serde"] }
|
||||
chrono = "0.4.42"
|
||||
dotenv = "0.15.0"
|
||||
reqwest = { version = "0.12", features = ["json", "multipart"] }
|
||||
tower-http = { version = "0.6", features = ["fs"] }
|
||||
|
|
@ -26,6 +26,3 @@ clap = { version = "4.5.51", features = ["derive"] }
|
|||
notify = "8.2.0"
|
||||
arc-swap = "1.7.1"
|
||||
regex = "1.12.2"
|
||||
postgres = { version = "0.19", features = ["with-chrono-0_4"] }
|
||||
bb8 = "0.9"
|
||||
bb8-postgres = "0.9"
|
||||
12
README.md
12
README.md
|
|
@ -1,8 +1,7 @@
|
|||
# tipi-lang
|
||||
|
||||
<img src="icon.svg" width="400" alt="tipi"/>
|
||||
|
||||
Tipi/teepee means: 'the place where they live/dwell' in Sioux/Dakota.
|
||||

|
||||
Tipi/teepee means: 'the place where they live' in Sioux/Dakota.
|
||||
see https://sesquiotic.com/2013/02/23/teepee/
|
||||
|
||||
Borrowing from that: 'the place where http lives'.
|
||||
|
|
@ -131,9 +130,8 @@ fn get() -> [Customer] | Customer? | ():
|
|||
* test support
|
||||
|
||||
## What about performance?
|
||||
* 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.
|
||||
* Clueless really! We'll see.
|
||||
* But it is written in rust
|
||||
* And it has no GC
|
||||
* So, maybe it will compete with python?
|
||||
|
||||
|
|
@ -233,5 +231,3 @@ fn add(a: string, b: string) -> string:
|
|||
ISSUES
|
||||
* Make everything an expression. If is a statement and so it can not be type checked
|
||||
* improve indenting
|
||||
|
||||
WIP guards
|
||||
|
|
|
|||
|
|
@ -4,4 +4,4 @@ fn fib(n: u64):
|
|||
else:
|
||||
fib(n-1) + fib(n-2)
|
||||
|
||||
println("fib = " + fib(38))
|
||||
println(fib(10))
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
* Example with postgres running
|
||||
* run ```cargo run -- --source examples/web --watch --postgres <connect_string>```
|
||||
* the connect_string is directly passed to bb8 like [here](https://github.com/djc/bb8/blob/main/postgres/examples/static_select.rs)
|
||||
* see ```main.tp``` for the database queries that it does on startup
|
||||
* and head to http://localhost:3000/api/customer
|
||||
* optionally, adjust the database ddl, update the query in db.tp, and recheck the output
|
||||
|
|
@ -1,2 +1,5 @@
|
|||
fn get_all() -> list:
|
||||
sql("select id, first_name, last_name from customers")
|
||||
fn get(id: u32) -> Customer:
|
||||
select id, first_name, last_name from customers where id = :id
|
||||
|
||||
fn save(c: Customer):
|
||||
insert into customers values(id, first_name, last_name) values(:c.id, :c.first_name, :c.last_name)
|
||||
|
|
|
|||
|
|
@ -1,2 +1,6 @@
|
|||
fn get_all() -> string:
|
||||
db::get_all()
|
||||
fn get(id: u32) -> Customer:
|
||||
let customer = dao.get(id)
|
||||
customer.date_fetched = current_date
|
||||
|
||||
fn add(customer: Customer):
|
||||
dao.save(customer)
|
||||
|
|
|
|||
|
|
@ -1,2 +1,17 @@
|
|||
fn get() -> list:
|
||||
service::get_all()
|
||||
fn get():
|
||||
| path == "/" -> list:
|
||||
service.get_all()
|
||||
| path == "/{uuid}" -> Customer?:
|
||||
service.get(uuid)?
|
||||
| path == "/" && query.firstname -> Customer?:
|
||||
service.get_by_firstname(fname)?
|
||||
| path == "/" && query.last_name -> Customer?
|
||||
service.get_by_lastname(lname)?
|
||||
| 404
|
||||
|
||||
fn post(customer: Customer):
|
||||
service.add(customer)
|
||||
|
||||
|
||||
fn put(customer: Customer):
|
||||
service.update(customer)
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
println("Creating the customers table")
|
||||
sql("create table if not exists customers(id serial primary key, first_name varchar(50), last_name varchar(50))")
|
||||
sql("insert into customers (first_name,last_name) values ('first', 'last')")
|
||||
|
|
@ -1,41 +1,18 @@
|
|||
use crate::DB_POOL;
|
||||
use std::cell::{RefMut};
|
||||
use crate::builtins::{FunctionMap, Signature, add};
|
||||
use crate::compiler::ast_pass::Parameter;
|
||||
use crate::compiler::tokens::TokenType;
|
||||
use crate::compiler::tokens::TokenType::{DateTime, ListType, StringType, Void};
|
||||
use crate::compiler::tokens::TokenType::{DateTime, StringType, Void};
|
||||
use crate::errors::RuntimeError;
|
||||
use crate::value::{Value, string};
|
||||
use std::cell::RefMut;
|
||||
use crate::value::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::LazyLock;
|
||||
use tokio_postgres::types::Type;
|
||||
use crate::compiler::ast_pass::Parameter;
|
||||
|
||||
pub(crate) static GLOBAL_FUNCTIONS: LazyLock<FunctionMap> = LazyLock::new(|| {
|
||||
let mut global_functions: FunctionMap = HashMap::new();
|
||||
let functions = &mut global_functions;
|
||||
add(functions, "now", Signature::new(vec![], DateTime, now));
|
||||
add(
|
||||
functions,
|
||||
"print",
|
||||
Signature::new(vec![Parameter::new("text", StringType)], Void, print),
|
||||
);
|
||||
add(
|
||||
functions,
|
||||
"println",
|
||||
Signature::new(vec![Parameter::new("text", StringType)], Void, println),
|
||||
);
|
||||
add(
|
||||
functions,
|
||||
"sql",
|
||||
Signature::new(
|
||||
vec![
|
||||
Parameter::new("query", StringType),
|
||||
Parameter::varargs(TokenType::Any),
|
||||
],
|
||||
ListType,
|
||||
sql,
|
||||
),
|
||||
);
|
||||
add(functions, "print", Signature::new(vec![Parameter::new("text", StringType)], Void, print));
|
||||
add(functions, "println", Signature::new(vec![Parameter::new("text", StringType)], Void, println));
|
||||
|
||||
global_functions
|
||||
});
|
||||
|
|
@ -54,55 +31,7 @@ fn print(_self_val: RefMut<Value>, args: Vec<Value>) -> Result<Value, RuntimeErr
|
|||
}
|
||||
|
||||
fn now(_self_val: RefMut<Value>, _args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
Ok(Value::DateTime(Box::new(chrono::Utc::now())))
|
||||
}
|
||||
|
||||
fn sql(_self_val: RefMut<Value>, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
let result = tokio::task::block_in_place(|| {
|
||||
tokio::runtime::Handle::current().block_on(async {
|
||||
let mut conn = DB_POOL.get()
|
||||
.expect(format!("Error running query '{}'. Did you add --postgres on the commandline?",args[0]).as_str())
|
||||
.get().await.unwrap();
|
||||
let result = conn.query(&args[0].to_string(), &[]).await.unwrap();
|
||||
let mut columns = vec![];
|
||||
|
||||
if let Some(first_row) = result.first() {
|
||||
let retrieved_columns = first_row.columns();
|
||||
for column in retrieved_columns {
|
||||
columns.push((column.name(), column.type_()));
|
||||
}
|
||||
}
|
||||
|
||||
let mut rows = vec![];
|
||||
for result_row in &result {
|
||||
let mut row: HashMap<Value, Value> = HashMap::new();
|
||||
for (index, _) in result_row.columns().iter().enumerate() {
|
||||
let column_name = columns.get(index).unwrap().0;
|
||||
let column_type = columns.get(index).unwrap().1;
|
||||
let value = match column_type {
|
||||
&Type::BOOL => Value::Bool(result_row.get(index)),
|
||||
&Type::INT2 | &Type::INT4 => Value::I32(result_row.get(index)),
|
||||
&Type::INT8 => Value::I64(result_row.get(index)),
|
||||
&Type::FLOAT4 => Value::F32(result_row.get(index)),
|
||||
&Type::FLOAT8 | &Type::NUMERIC => Value::F64(result_row.get(index)),
|
||||
&Type::CHAR
|
||||
| &Type::VARCHAR
|
||||
| &Type::TEXT
|
||||
| &Type::BPCHAR
|
||||
| &Type::NAME => Value::String(result_row.get(index)),
|
||||
&Type::TIMESTAMP | &Type::TIMESTAMPTZ => {
|
||||
Value::DateTime(Box::new(result_row.get(index)))
|
||||
}
|
||||
&Type::UUID => Value::Uuid(result_row.get(index)),
|
||||
_ => unimplemented!("database type {:?}", column_type),
|
||||
};
|
||||
|
||||
row.insert(string(column_name), value);
|
||||
}
|
||||
rows.push(Value::Map(row));
|
||||
}
|
||||
rows
|
||||
})
|
||||
});
|
||||
Ok(Value::List(result))
|
||||
Ok(Value::DateTime(Box::new(
|
||||
chrono::Utc::now(),
|
||||
)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,11 +22,6 @@ 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
|
||||
}
|
||||
|
||||
|
|
@ -60,19 +55,6 @@ 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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,64 +0,0 @@
|
|||
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,19 +1,17 @@
|
|||
pub(crate) mod globals;
|
||||
mod list;
|
||||
mod map;
|
||||
mod string;
|
||||
mod list;
|
||||
pub(crate) mod globals;
|
||||
|
||||
use crate::builtins::list::list_functions;
|
||||
use crate::builtins::map::map_functions;
|
||||
use crate::builtins::string::string_functions;
|
||||
use crate::compiler::ast_pass::Parameter;
|
||||
use crate::compiler::tokens::TokenType;
|
||||
use crate::errors::{CompilerError, RuntimeError};
|
||||
use crate::value::Value;
|
||||
use std::cell::{RefCell, RefMut};
|
||||
use crate::builtins::string::string_functions;
|
||||
use crate::errors::{CompilerError, RuntimeError};
|
||||
use crate::compiler::tokens::TokenType;
|
||||
use crate::value::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::sync::LazyLock;
|
||||
use crate::compiler::ast_pass::Parameter;
|
||||
use crate::builtins::list::list_functions;
|
||||
|
||||
pub(crate) struct Signature {
|
||||
pub(crate) parameters: Vec<Parameter>,
|
||||
|
|
@ -34,12 +32,8 @@ impl Signature {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn arity(&self) -> u8 {
|
||||
self.parameters.len() as u8
|
||||
}
|
||||
|
||||
pub(crate) fn has_varargs(&self) -> bool {
|
||||
!self.parameters.is_empty() && self.parameters.last().unwrap().name.lexeme == "varargs"
|
||||
pub(crate) fn arity(&self) -> usize {
|
||||
self.parameters.len()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -53,7 +47,6 @@ 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
|
||||
});
|
||||
|
|
@ -62,10 +55,7 @@ pub(crate) fn add(m: &mut FunctionMap, name: &str, method: Signature) {
|
|||
m.insert(name.to_string(), method);
|
||||
}
|
||||
|
||||
pub(crate) fn lookup(
|
||||
type_name: &str,
|
||||
method_name: &str,
|
||||
) -> Result<&'static Signature, CompilerError> {
|
||||
pub(crate) fn lookup(type_name: &str, method_name: &str) -> Result<&'static Signature, CompilerError> {
|
||||
FUNCTIONS
|
||||
.get(type_name)
|
||||
.and_then(|methods| methods.get(method_name))
|
||||
|
|
@ -78,9 +68,7 @@ pub(crate) fn call(
|
|||
self_val: Rc<RefCell<Value>>,
|
||||
args: Vec<Value>,
|
||||
) -> Result<Value, RuntimeError> {
|
||||
(lookup(type_name, method_name)
|
||||
.map_err(|e| RuntimeError::FunctionNotFound(e.to_string()))?
|
||||
.function)(self_val.borrow_mut(), args)
|
||||
(lookup(type_name,method_name).map_err(|e|RuntimeError::FunctionNotFound(e.to_string()))?.function)(self_val.borrow_mut(), args)
|
||||
}
|
||||
|
||||
pub(crate) fn expected(expected_type: &str) -> RuntimeError {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ use crate::value::Value;
|
|||
use crate::{AsmRegistry, SymbolTable};
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
use crate::compiler::namespace::Namespace;
|
||||
|
||||
pub fn compile(
|
||||
qualified_name: Option<&str>,
|
||||
|
|
@ -31,7 +30,7 @@ pub fn compile_function(
|
|||
function: &Function,
|
||||
symbols: &SymbolTable,
|
||||
registry: &mut AsmRegistry,
|
||||
namespace: &Namespace,
|
||||
namespace: &str,
|
||||
) -> Result<AsmChunk, CompilerErrorAtLine> {
|
||||
let fn_name = &function.name.lexeme;
|
||||
let mut compiler = AsmPass::new(fn_name);
|
||||
|
|
@ -54,7 +53,7 @@ pub fn compile_in_namespace(
|
|||
) -> Result<(), CompilerErrorAtLine> {
|
||||
let name = namespace.unwrap_or("main");
|
||||
let mut compiler = AsmPass::new(name);
|
||||
let chunk = compiler.compile(ast, symbols, registry, &Namespace::new(name))?;
|
||||
let chunk = compiler.compile(ast, symbols, registry, name)?;
|
||||
registry.insert(name.to_string(), chunk);
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -137,7 +136,7 @@ impl AsmPass {
|
|||
ast: &Vec<Statement>,
|
||||
symbols: &SymbolTable,
|
||||
registry: &mut AsmRegistry,
|
||||
namespace: &Namespace,
|
||||
namespace: &str,
|
||||
) -> Result<AsmChunk, CompilerErrorAtLine> {
|
||||
self.compile_statements(ast, symbols, registry, namespace)?;
|
||||
self.emit(Return);
|
||||
|
|
@ -152,7 +151,7 @@ impl AsmPass {
|
|||
ast: &Vec<Statement>,
|
||||
symbols: &SymbolTable,
|
||||
registry: &mut AsmRegistry,
|
||||
namespace: &Namespace,
|
||||
namespace: &str,
|
||||
) -> Result<(), CompilerErrorAtLine> {
|
||||
for statement in ast {
|
||||
self.compile_statement(statement, symbols, registry, namespace)?;
|
||||
|
|
@ -166,7 +165,7 @@ impl AsmPass {
|
|||
statement: &Statement,
|
||||
symbols: &SymbolTable,
|
||||
registry: &mut AsmRegistry,
|
||||
namespace: &Namespace,
|
||||
namespace: &str,
|
||||
) -> Result<(), CompilerErrorAtLine> {
|
||||
self.current_line = statement.line();
|
||||
match statement {
|
||||
|
|
@ -194,7 +193,7 @@ impl AsmPass {
|
|||
|
||||
fn compile_expression(
|
||||
&mut self,
|
||||
namespace: &Namespace,
|
||||
namespace: &str,
|
||||
expression: &Expression,
|
||||
symbols: &SymbolTable,
|
||||
registry: &mut AsmRegistry,
|
||||
|
|
@ -259,8 +258,8 @@ impl AsmPass {
|
|||
Expression::FunctionCall {
|
||||
name, arguments, ..
|
||||
} => {
|
||||
let qname = format!("{}/{}", namespace.pop(), name.replace("::","/"));
|
||||
let function = symbols.get(&qname);
|
||||
let qname = format!("{}/{}", namespace, name);
|
||||
let function = symbols.get(&qname).or(symbols.get(name));
|
||||
match function {
|
||||
Some(Symbol::Function { parameters, .. }) => {
|
||||
let name_index = self.chunk.find_constant(&qname).unwrap_or_else(|| {
|
||||
|
|
@ -271,7 +270,7 @@ impl AsmPass {
|
|||
namespace, symbols, registry, arguments, parameters,
|
||||
)?;
|
||||
|
||||
self.emit(Call(name_index, arguments.len() as u8));
|
||||
self.emit(Call(name_index, arguments.len()));
|
||||
}
|
||||
// constructor function
|
||||
Some(Symbol::Object { fields, .. }) => {
|
||||
|
|
@ -281,7 +280,7 @@ impl AsmPass {
|
|||
self.get_arguments_in_order(
|
||||
namespace, symbols, registry, arguments, fields,
|
||||
)?;
|
||||
self.emit(Call(name_index, arguments.len() as u8));
|
||||
self.emit(Call(name_index, arguments.len()));
|
||||
}
|
||||
// maybe global function
|
||||
_ => {
|
||||
|
|
@ -296,12 +295,7 @@ impl AsmPass {
|
|||
let name_index = self.chunk.find_constant(name).unwrap_or_else(|| {
|
||||
self.chunk.add_constant(Value::String(name.to_string()))
|
||||
});
|
||||
let arity = if fun.has_varargs() {
|
||||
arguments.len() as u8
|
||||
} else {
|
||||
fun.arity()
|
||||
};
|
||||
self.emit(Call(name_index, arity));
|
||||
self.emit(Call(name_index, fun.arity()));
|
||||
} else {
|
||||
return Err(self
|
||||
.error_at_line(CompilerError::FunctionNotFound(name.to_string())));
|
||||
|
|
@ -331,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() as u8 && !signature.has_varargs() {
|
||||
if signature.arity() != arguments.len() {
|
||||
return Err(self.error_at_line(CompilerError::IllegalArgumentsException(
|
||||
format!("{}.{}", receiver_type, method_name),
|
||||
signature.parameters.len(),
|
||||
|
|
@ -345,7 +339,7 @@ impl AsmPass {
|
|||
arguments,
|
||||
&signature.parameters,
|
||||
)?;
|
||||
self.emit(CallBuiltin(name_index, type_index, arguments.len() as u8));
|
||||
self.emit(CallBuiltin(name_index, type_index, arguments.len()));
|
||||
}
|
||||
Expression::Variable { name, .. } => {
|
||||
let name_index = self.vars.get(name);
|
||||
|
|
@ -450,7 +444,11 @@ impl AsmPass {
|
|||
}
|
||||
Expression::MapGet { .. } => {}
|
||||
Expression::FieldGet { .. } => {}
|
||||
Expression::Range { lower, .. } => {}
|
||||
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::ForStatement {
|
||||
loop_var,
|
||||
range,
|
||||
|
|
@ -497,7 +495,7 @@ impl AsmPass {
|
|||
// named parameters do not have to be passed in order, but they do need to be evaluated in the order of the called function/constructor
|
||||
fn get_arguments_in_order(
|
||||
&mut self,
|
||||
namespace: &Namespace,
|
||||
namespace: &str,
|
||||
symbols: &SymbolTable,
|
||||
registry: &mut AsmRegistry,
|
||||
arguments: &[Expression],
|
||||
|
|
@ -552,7 +550,7 @@ pub enum Op {
|
|||
Divide,
|
||||
Negate,
|
||||
Return,
|
||||
Call(usize, u8),
|
||||
Call(usize, usize),
|
||||
And,
|
||||
Or,
|
||||
Not,
|
||||
|
|
@ -573,7 +571,7 @@ pub enum Op {
|
|||
DefMap(usize),
|
||||
Assign(usize),
|
||||
ListGet,
|
||||
CallBuiltin(usize, usize, u8),
|
||||
CallBuiltin(usize, usize, usize),
|
||||
Dup,
|
||||
GotoIf(usize),
|
||||
GotoIfNot(usize),
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
use crate::builtins::globals::GLOBAL_FUNCTIONS;
|
||||
use crate::compiler::ast_pass::Expression::{
|
||||
Assignment, FieldGet, FunctionCall, IfElseExpression, IfExpression, LetExpression, ListGet,
|
||||
MapGet, MethodCall, NamedParameter, Stop, Variable,
|
||||
};
|
||||
use crate::compiler::tokens::TokenType::{
|
||||
Bang, Bool, Char, Colon, DateTime, Dot, Else, Eof, Eol, Equal, False, FloatingPoint, Fn, For,
|
||||
Greater, GreaterEqual, GreaterGreater, Identifier, If, In, Indent, Integer, LeftBrace,
|
||||
|
|
@ -147,7 +151,7 @@ impl AstCompiler {
|
|||
Err(self.error_at_line(Expected("-> or ?")))
|
||||
};
|
||||
}
|
||||
Ok(Expression::Stop {
|
||||
Ok(Stop {
|
||||
line: self.peek().line,
|
||||
})
|
||||
}
|
||||
|
|
@ -158,7 +162,7 @@ impl AstCompiler {
|
|||
self.consume(&RightBrace, Expected("'}' after guard expression."))?;
|
||||
Ok(query_params)
|
||||
} else {
|
||||
Ok(Expression::Stop {
|
||||
Ok(Stop {
|
||||
line: self.peek().line,
|
||||
})
|
||||
}
|
||||
|
|
@ -170,7 +174,7 @@ impl AstCompiler {
|
|||
self.consume(&RightBrace, Expected("'}' after guard expression."))?;
|
||||
Ok(path_params)
|
||||
} else {
|
||||
Ok(Expression::Stop {
|
||||
Ok(Stop {
|
||||
line: self.peek().line,
|
||||
})
|
||||
}
|
||||
|
|
@ -343,7 +347,7 @@ impl AstCompiler {
|
|||
},
|
||||
);
|
||||
|
||||
Ok(Expression::LetExpression {
|
||||
Ok(LetExpression {
|
||||
name: name_token,
|
||||
var_type,
|
||||
initializer: Box::new(initializer),
|
||||
|
|
@ -387,8 +391,8 @@ impl AstCompiler {
|
|||
if self.match_token(&[Equal]) {
|
||||
let operator = self.previous().clone();
|
||||
let right = self.comparison(symbol_table)?;
|
||||
if let Expression::Variable { name, .. } = expr {
|
||||
Ok(Expression::Assignment {
|
||||
if let Variable { name, .. } = expr {
|
||||
Ok(Assignment {
|
||||
line: operator.line,
|
||||
variable_name: name.to_string(),
|
||||
value: Box::new(right),
|
||||
|
|
@ -495,13 +499,13 @@ impl AstCompiler {
|
|||
|
||||
self.inc_indent();
|
||||
|
||||
Ok(Expression::IfElseExpression {
|
||||
Ok(IfElseExpression {
|
||||
condition: Box::new(condition),
|
||||
then_branch,
|
||||
else_branch: self.compile(symbol_table)?,
|
||||
})
|
||||
} else {
|
||||
Ok(Expression::IfExpression {
|
||||
Ok(IfExpression {
|
||||
condition: Box::new(condition),
|
||||
then_branch,
|
||||
})
|
||||
|
|
@ -530,23 +534,22 @@ impl AstCompiler {
|
|||
}
|
||||
}
|
||||
|
||||
// index into list
|
||||
fn index(&mut self, operand: Expression, index: Expression) -> Expr {
|
||||
let get = match &operand {
|
||||
Expression::Map { .. } => Expression::MapGet {
|
||||
Expression::Map { .. } => MapGet {
|
||||
map: Box::new(operand),
|
||||
key: Box::new(index),
|
||||
},
|
||||
Expression::List { .. } => Expression::ListGet {
|
||||
Expression::List { .. } => ListGet {
|
||||
list: Box::new(operand),
|
||||
index: Box::new(index),
|
||||
},
|
||||
Expression::Variable { var_type, .. } => match var_type {
|
||||
ListType => Expression::ListGet {
|
||||
Variable { var_type, .. } => match var_type {
|
||||
ListType => ListGet {
|
||||
list: Box::new(operand),
|
||||
index: Box::new(index),
|
||||
},
|
||||
MapType => Expression::MapGet {
|
||||
MapType => MapGet {
|
||||
map: Box::new(operand),
|
||||
key: Box::new(index),
|
||||
},
|
||||
|
|
@ -575,7 +578,7 @@ impl AstCompiler {
|
|||
) -> Expr {
|
||||
if self.match_token(&[LeftParen]) {
|
||||
let arguments = self.arguments(symbol_table)?;
|
||||
Ok(Expression::MethodCall {
|
||||
Ok(MethodCall {
|
||||
receiver: Box::new(receiver.clone()),
|
||||
method_name: op.lexeme,
|
||||
arguments,
|
||||
|
|
@ -583,7 +586,7 @@ impl AstCompiler {
|
|||
})
|
||||
} else {
|
||||
// no test yet
|
||||
Ok(Expression::FieldGet {
|
||||
Ok(FieldGet {
|
||||
receiver: Box::new(receiver.clone()),
|
||||
field: op.lexeme.clone(),
|
||||
})
|
||||
|
|
@ -695,7 +698,7 @@ impl AstCompiler {
|
|||
fn named_parameter(&mut self, name: &Token, symbol_table: &mut SymbolTable) -> Expr {
|
||||
let value = self.expression(symbol_table)?;
|
||||
let line = name.line;
|
||||
Ok(Expression::NamedParameter {
|
||||
Ok(NamedParameter {
|
||||
name: name.clone(),
|
||||
value: Box::new(value),
|
||||
line,
|
||||
|
|
@ -748,7 +751,7 @@ impl AstCompiler {
|
|||
} else {
|
||||
&Unknown
|
||||
};
|
||||
Ok(Expression::Variable {
|
||||
Ok(Variable {
|
||||
name: name.lexeme.to_string(),
|
||||
var_type: var_type.clone(),
|
||||
line: name.line,
|
||||
|
|
@ -757,7 +760,7 @@ impl AstCompiler {
|
|||
|
||||
fn function_call(&mut self, name: Token, symbol_table: &mut SymbolTable) -> Expr {
|
||||
let arguments = self.arguments(symbol_table)?;
|
||||
Ok(Expression::FunctionCall {
|
||||
Ok(FunctionCall {
|
||||
line: self.peek().line,
|
||||
name: name.lexeme.to_string(),
|
||||
arguments,
|
||||
|
|
@ -888,24 +891,13 @@ impl Parameter {
|
|||
pub fn new(name: impl Into<String>, value_type: TokenType) -> Self {
|
||||
Self {
|
||||
name: Token {
|
||||
token_type: StringType,
|
||||
token_type: TokenType::StringType,
|
||||
lexeme: name.into(),
|
||||
line: 0,
|
||||
},
|
||||
var_type: value_type,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn varargs(token_type: TokenType) -> Self {
|
||||
Self {
|
||||
name: Token {
|
||||
token_type: StringType,
|
||||
lexeme: "varargs".into(),
|
||||
line: 0,
|
||||
},
|
||||
var_type: token_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
@ -1017,19 +1009,19 @@ impl Expression {
|
|||
Self::Range { line, .. } => *line,
|
||||
Self::List { line, .. } => *line,
|
||||
Self::Map { line, .. } => *line,
|
||||
Self::Variable { line, .. } => *line,
|
||||
Self::Assignment { line, .. } => *line,
|
||||
Self::FunctionCall { line, .. } => *line,
|
||||
Self::MethodCall { line, .. } => *line,
|
||||
Self::Stop { line } => *line,
|
||||
Self::NamedParameter { line, .. } => *line,
|
||||
Self::MapGet { .. } => 0,
|
||||
Self::ListGet { .. } => 0,
|
||||
Self::FieldGet { .. } => 0,
|
||||
Self::IfExpression { condition, .. } => condition.line(),
|
||||
Self::IfElseExpression { condition, .. } => condition.line(),
|
||||
Self::LetExpression { name, .. } => name.line,
|
||||
Self::ForStatement { loop_var, .. } => loop_var.line,
|
||||
Variable { line, .. } => *line,
|
||||
Assignment { line, .. } => *line,
|
||||
FunctionCall { line, .. } => *line,
|
||||
MethodCall { line, .. } => *line,
|
||||
Stop { line } => *line,
|
||||
NamedParameter { line, .. } => *line,
|
||||
MapGet { .. } => 0,
|
||||
ListGet { .. } => 0,
|
||||
FieldGet { .. } => 0,
|
||||
IfExpression { condition, .. } => condition.line(),
|
||||
IfElseExpression { condition, .. } => condition.line(),
|
||||
LetExpression { name, .. } => name.line,
|
||||
Expression::ForStatement { loop_var, .. } => loop_var.line,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
mod tests {
|
||||
use crate::DATE_FORMAT_TIMEZONE;
|
||||
use crate::compiler::{compile, run};
|
||||
use crate::errors::CompilerError::{IllegalArgumentsException, ReservedFunctionName, UndeclaredVariable};
|
||||
use crate::errors::CompilerError::{IllegalArgumentsException, ReservedFunctionName};
|
||||
use crate::errors::CompilerErrorAtLine;
|
||||
use crate::errors::RuntimeError::{IllegalArgumentException, IndexOutOfBounds, NotSortable};
|
||||
use crate::errors::RuntimeError::{IllegalArgumentException, IndexOutOfBounds};
|
||||
use crate::errors::TipiLangError::{Compiler, Runtime};
|
||||
use crate::value::{Value, i64, string};
|
||||
use crate::value::{Value, string};
|
||||
use chrono::DateTime;
|
||||
|
||||
#[test]
|
||||
|
|
@ -299,28 +299,6 @@ 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!(
|
||||
|
|
@ -471,14 +449,12 @@ let a:i64 = if true:
|
|||
42
|
||||
else:
|
||||
0
|
||||
a"#)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn package() {
|
||||
assert_eq!(run(r#"a::b.c()"#), Err(Compiler(CompilerErrorAtLine {error: UndeclaredVariable("a::b".to_string()), line: 1 })));
|
||||
a"#).unwrap();
|
||||
}
|
||||
// #[test]
|
||||
// fn package() {
|
||||
// assert_eq!(run(r#"a.b.c()"#), Ok(Value::U32(48)));
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn guards() {
|
||||
|
|
@ -488,19 +464,4 @@ a"#)
|
|||
// Ok(Value::Void)
|
||||
// );
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn query() {
|
||||
let result =
|
||||
run(r#"sql("select id, first_name, last_name from customers where id = 1")"#).unwrap();
|
||||
if let Value::List(records) = result {
|
||||
assert_eq!(records.len(), 1);
|
||||
if let Value::Map(record) = records.get(0).unwrap() {
|
||||
assert_eq!(record.get(&string("id")).unwrap(), &i64(1));
|
||||
assert_eq!(record.get(&string("first_name")).unwrap(), &string("sander"));
|
||||
assert_eq!(record.get(&string("last_name")).unwrap(), &string("hautvast"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use log::info;
|
||||
use walkdir::WalkDir;
|
||||
use crate::{symbol_builder, AsmRegistry, TIPI_EXT};
|
||||
use crate::compiler::assembly_pass::AsmChunk;
|
||||
|
|
@ -12,18 +11,17 @@ pub mod scan_pass;
|
|||
pub mod ast_pass;
|
||||
pub mod tokens;
|
||||
pub mod assembly_pass;
|
||||
pub(crate) mod namespace;
|
||||
|
||||
pub fn compile_sourcedir(source_dir: &str) -> Result<HashMap<String, AsmChunk>, TipiLangError> {
|
||||
let mut asm_registry = AsmRegistry::new();
|
||||
let mut symbol_table = HashMap::new();
|
||||
|
||||
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) {
|
||||
info!("-- Compiling {} -- ", path);
|
||||
print!("-- 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();
|
||||
match ast_pass::compile(Some(path), tokens, &mut symbol_table) {
|
||||
Ok(statements) => {
|
||||
let path = path.strip_prefix(source_dir).unwrap().replace(TIPI_EXT, "");
|
||||
|
|
@ -65,5 +63,5 @@ pub fn run(src: &str) -> Result<crate::value::Value, TipiLangError> {
|
|||
let mut asm_registry = HashMap::new();
|
||||
assembly_pass::compile(None, &ast, &symbol_table, &mut asm_registry)?;
|
||||
let registry = arc_swap::ArcSwap::from(std::sync::Arc::new(asm_registry));
|
||||
crate::vm::run(registry.load(), "main").map_err(TipiLangError::from)
|
||||
crate::vm::interpret(registry.load(), "main").map_err(TipiLangError::from)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
pub(crate) struct Namespace {
|
||||
elements: Vec<String>,
|
||||
}
|
||||
|
||||
impl Namespace {
|
||||
pub(crate) fn new(path: &str) -> Self {
|
||||
Self {
|
||||
elements: path.split('/').map(|path| path.to_string()).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn pop(&self) -> Self{
|
||||
let mut copy = self.elements.to_vec();
|
||||
copy.pop();
|
||||
Self{
|
||||
elements: copy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Namespace {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.elements.join("/"))
|
||||
}
|
||||
}
|
||||
|
|
@ -87,11 +87,7 @@ impl Scanner {
|
|||
}
|
||||
'#' => self.add_token(TokenType::Hash),
|
||||
'+' => self.add_token(TokenType::Plus),
|
||||
':' => {
|
||||
if !self.match_next(':') {
|
||||
self.add_token(TokenType::Colon);
|
||||
}
|
||||
}
|
||||
':' => self.add_token(TokenType::Colon),
|
||||
';' => println!("Warning: Ignoring semicolon at line {}", self.line),
|
||||
'*' => self.add_token(TokenType::Star),
|
||||
'!' => {
|
||||
|
|
@ -186,13 +182,7 @@ impl Scanner {
|
|||
}
|
||||
|
||||
fn identifier(&mut self) {
|
||||
while is_alphanumeric(self.peek())
|
||||
|| self.peek() == '_'
|
||||
|| (self.peek() == ':' && self.peek_next() == ':')
|
||||
{
|
||||
if self.peek() == ':' && self.peek_next() == ':' {
|
||||
self.advance();
|
||||
}
|
||||
while is_alphanumeric(self.peek()) {
|
||||
self.advance();
|
||||
}
|
||||
let value: String = self.chars[self.start..self.current].iter().collect();
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ pub enum TokenType {
|
|||
Bool,
|
||||
Char,
|
||||
Colon,
|
||||
ColonColon,
|
||||
Comma,
|
||||
DateTime,
|
||||
Dot,
|
||||
|
|
@ -114,7 +113,6 @@ impl fmt::Display for TokenType {
|
|||
TokenType::Pipe => write!(f, "|"),
|
||||
TokenType::BitXor => write!(f, "^"),
|
||||
TokenType::Colon => write!(f, ":"),
|
||||
TokenType::ColonColon => write!(f, "::"),
|
||||
TokenType::Comma => write!(f, ","),
|
||||
TokenType::FloatingPoint => write!(f, "float"),
|
||||
TokenType::MapType => write!(f, "map"),
|
||||
|
|
|
|||
|
|
@ -96,8 +96,6 @@ 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)]
|
||||
|
|
|
|||
|
|
@ -17,13 +17,12 @@ pub fn start_watch_daemon(source: &str, registry: Arc<ArcSwap<HashMap<String, As
|
|||
let s = source.to_string();
|
||||
let (tx, rx) = channel();
|
||||
thread::spawn(move || {
|
||||
info!("-- File watch started --");
|
||||
println!("-- File watch started --");
|
||||
let path = Path::new(&source);
|
||||
if !path.exists() {
|
||||
panic!("source directory {} does not exist", &source);
|
||||
}
|
||||
let mut watcher = notify::recommended_watcher(tx)
|
||||
.expect("Failed to create watcher");
|
||||
let mut watcher = notify::recommended_watcher(tx).expect("Failed to create watcher");
|
||||
watcher
|
||||
.watch(path, RecursiveMode::Recursive)
|
||||
.expect("Failed to watch");
|
||||
|
|
|
|||
|
|
@ -3,9 +3,6 @@ use crate::compiler::ast_pass::{Expression, Statement};
|
|||
use crate::errors::CompilerErrorAtLine;
|
||||
use crate::symbol_builder::Symbol;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::OnceLock;
|
||||
use bb8_postgres::PostgresConnectionManager;
|
||||
use tokio_postgres::NoTls;
|
||||
|
||||
mod builtins;
|
||||
pub mod compiler;
|
||||
|
|
@ -14,7 +11,7 @@ pub mod file_watch;
|
|||
mod keywords;
|
||||
pub mod repl;
|
||||
mod symbol_builder;
|
||||
pub mod value;
|
||||
mod value;
|
||||
pub mod vm;
|
||||
|
||||
pub(crate) type SymbolTable = HashMap<String, Symbol>;
|
||||
|
|
@ -24,5 +21,3 @@ pub(crate) type AsmRegistry = HashMap<String, AsmChunk>;
|
|||
|
||||
pub const TIPI_EXT: &str = ".tp";
|
||||
pub const DATE_FORMAT_TIMEZONE: &str = "%Y-%m-%d %H:%M:%S%.3f %z";
|
||||
|
||||
pub static DB_POOL: OnceLock<bb8::Pool<PostgresConnectionManager<NoTls>>> = OnceLock::new();
|
||||
|
|
|
|||
44
src/main.rs
44
src/main.rs
|
|
@ -3,20 +3,15 @@ use axum::extract::{Request, State};
|
|||
use axum::http::StatusCode;
|
||||
use axum::routing::any;
|
||||
use axum::{Json, Router};
|
||||
use bb8_postgres::PostgresConnectionManager;
|
||||
use clap::Parser;
|
||||
use log::{error, info};
|
||||
use log::info;
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::sync::{Arc, LazyLock, OnceLock};
|
||||
use std::sync::Arc;
|
||||
use tipi_lang::compiler::assembly_pass::AsmChunk;
|
||||
use tipi_lang::compiler::{compile_sourcedir, map_underlying, run};
|
||||
use tipi_lang::errors::TipiLangError;
|
||||
use tipi_lang::value::Value;
|
||||
use tipi_lang::vm::run_async;
|
||||
use tipi_lang::{DB_POOL, vm};
|
||||
use tokio_postgres::NoTls;
|
||||
use std::str::FromStr;
|
||||
use tipi_lang::vm::interpret_async;
|
||||
|
||||
/// A simple CLI tool to greet users
|
||||
#[derive(Parser, Debug)]
|
||||
|
|
@ -32,46 +27,27 @@ struct Args {
|
|||
|
||||
#[arg(short, long)]
|
||||
file: Option<String>,
|
||||
|
||||
#[arg(short, long)]
|
||||
postgres: Option<String>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), TipiLangError> {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let args = Args::parse();
|
||||
if let Some(pg) = args.postgres {
|
||||
let config = tokio_postgres::config::Config::from_str(&pg).map_err(|e|TipiLangError::Platform(e.to_string()))?;
|
||||
let manager = PostgresConnectionManager::new(config,NoTls);
|
||||
let pool = bb8::Pool::builder()
|
||||
.build(manager)
|
||||
.await
|
||||
.expect("cannot create the the database connection pool");
|
||||
let _ = DB_POOL.set(pool);
|
||||
}
|
||||
|
||||
if let Some(file) = args.file {
|
||||
let source = fs::read_to_string(file).expect("Unable to read file");
|
||||
run(&source)?;
|
||||
} else {
|
||||
info!("-- Tipilang --");
|
||||
println!("-- Tipilang --");
|
||||
let source = args.source.unwrap_or("./source".to_string());
|
||||
let registry = compile_sourcedir(&source)?;
|
||||
let empty = registry.is_empty();
|
||||
|
||||
let swap = Arc::new(ArcSwap::from(Arc::new(registry)));
|
||||
if !empty {
|
||||
let registry = swap.clone().load();
|
||||
if registry.get("/main").is_some() {
|
||||
vm::run(registry, "/main")?;
|
||||
}
|
||||
|
||||
if args.watch {
|
||||
tipi_lang::file_watch::start_watch_daemon(&source, swap.clone());
|
||||
}
|
||||
info!("-- Compilation successful --");
|
||||
println!("-- Compilation successful --");
|
||||
let state = AppState {
|
||||
registry: swap.clone(),
|
||||
};
|
||||
|
|
@ -83,7 +59,7 @@ async fn main() -> Result<(), TipiLangError> {
|
|||
.await
|
||||
.map_err(map_underlying())?;
|
||||
|
||||
info!(
|
||||
println!(
|
||||
"-- Listening on {} --\n",
|
||||
listener.local_addr().map_err(map_underlying())?
|
||||
);
|
||||
|
|
@ -94,7 +70,7 @@ async fn main() -> Result<(), TipiLangError> {
|
|||
|
||||
axum::serve(listener, app).await.map_err(map_underlying())?;
|
||||
} else {
|
||||
error!("No source files found or compilation error");
|
||||
println!("No source files found or compilation error");
|
||||
if args.repl {
|
||||
tipi_lang::repl::start(swap.clone())?;
|
||||
}
|
||||
|
|
@ -111,7 +87,7 @@ struct AppState {
|
|||
async fn handle_any(
|
||||
State(state): State<AppState>,
|
||||
req: Request,
|
||||
) -> Result<Json<Value>, StatusCode> {
|
||||
) -> Result<Json<String>, StatusCode> {
|
||||
let method = req.method().to_string().to_ascii_lowercase();
|
||||
let uri = req.uri();
|
||||
|
||||
|
|
@ -133,7 +109,7 @@ async fn handle_any(
|
|||
}
|
||||
let path = &req.uri().to_string();
|
||||
info!("invoked {:?} => {}", req, function_qname);
|
||||
match run_async(
|
||||
match interpret_async(
|
||||
state.registry.load(),
|
||||
&function_qname,
|
||||
path,
|
||||
|
|
@ -142,7 +118,7 @@ async fn handle_any(
|
|||
)
|
||||
.await
|
||||
{
|
||||
Ok(value) => Ok(Json(value)),
|
||||
Ok(value) => Ok(Json(value.to_string())),
|
||||
Err(_) => {
|
||||
// url checks out but function for method not found
|
||||
if state
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ use std::io;
|
|||
use std::io::Write;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use crate::compiler::namespace::Namespace;
|
||||
|
||||
pub fn start(registry: Arc<ArcSwap<HashMap<String, AsmChunk>>>) -> Result<(), TipiLangError> {
|
||||
println!("REPL started -- Type ctrl-c to exit (both the repl and the server)");
|
||||
|
|
@ -45,7 +44,7 @@ pub fn start(registry: Arc<ArcSwap<HashMap<String, AsmChunk>>>) -> Result<(), Ti
|
|||
};
|
||||
symbol_builder::build("", &ast, &mut symbol_table);
|
||||
|
||||
match asm_pass.compile(&ast, &symbol_table, &mut registry_copy, &Namespace::new("")) {
|
||||
match asm_pass.compile(&ast, &symbol_table, &mut registry_copy, "") {
|
||||
Ok(chunk) => {
|
||||
registry_copy.insert("main".to_string(), chunk);
|
||||
registry.store(Arc::new(registry_copy));
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ fn make_qname(path: &str, name: &Token) -> String {
|
|||
if path.is_empty() {
|
||||
name.lexeme.to_string()
|
||||
} else {
|
||||
format!("{}/{}", path, name.lexeme)
|
||||
format!("{}.{}", path, name.lexeme)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
55
src/value.rs
55
src/value.rs
|
|
@ -1,21 +1,19 @@
|
|||
use crate::DATE_FORMAT_TIMEZONE;
|
||||
use crate::errors::ValueError;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::ser::{SerializeMap, SerializeSeq};
|
||||
use serde::{Deserialize, Serialize, Serializer};
|
||||
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};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Object {
|
||||
pub(crate) definition: String,
|
||||
pub(crate) fields: Vec<(String, Value)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Value {
|
||||
U32(u32),
|
||||
I32(i32),
|
||||
|
|
@ -33,41 +31,13 @@ pub enum Value {
|
|||
ObjectType(Box<Object>),
|
||||
Error(String),
|
||||
Void,
|
||||
Uuid(String),
|
||||
}
|
||||
|
||||
impl Serialize for Value {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match self {
|
||||
Value::String(s) => serializer.serialize_str(s),
|
||||
Value::I32(n) => serializer.serialize_i32(*n),
|
||||
Value::I64(n) => serializer.serialize_i64(*n),
|
||||
Value::U32(n) => serializer.serialize_u32(*n),
|
||||
Value::U64(n) => serializer.serialize_u64(*n),
|
||||
Value::F32(f) => serializer.serialize_f32(*f),
|
||||
Value::F64(f) => serializer.serialize_f64(*f),
|
||||
Value::Void => serializer.serialize_unit(),
|
||||
Value::Bool(b) => serializer.serialize_bool(*b),
|
||||
Value::Char(c) => serializer.serialize_char(*c),
|
||||
Value::DateTime(d) => serializer.serialize_str(&d.to_string()),
|
||||
Value::Enum => unimplemented!("TODO implement serialize enums"),
|
||||
Value::Error(s) => serializer.serialize_str(s),
|
||||
Value::List(vec) => vec.serialize(serializer),
|
||||
Value::Map(map) => map.serialize(serializer),
|
||||
Value::ObjectType(obj) => obj.serialize(serializer),
|
||||
Value::Uuid(uuid) => serializer.serialize_str(uuid),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
|
|
@ -139,24 +109,6 @@ 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 {
|
||||
|
|
@ -247,7 +199,6 @@ impl Display for Value {
|
|||
Value::Map(map) => to_string(f, map),
|
||||
Value::Error(v) => write!(f, "{}", v),
|
||||
Value::Void => write!(f, "()"),
|
||||
Value::Uuid(v) => write!(f, "{}", v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use std::collections::HashMap;
|
|||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub async fn run_async(
|
||||
pub async fn interpret_async(
|
||||
registry: Guard<Arc<HashMap<String, AsmChunk>>>,
|
||||
function: &str,
|
||||
uri: &str,
|
||||
|
|
@ -38,13 +38,13 @@ pub async fn run_async(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn run(registry: Guard<Arc<AsmRegistry>>, function: &str) -> Result<Value, RuntimeError> {
|
||||
pub fn interpret(registry: Guard<Arc<AsmRegistry>>, function: &str) -> Result<Value, RuntimeError> {
|
||||
let chunk = registry.get(function).unwrap().clone();
|
||||
let mut vm = Vm::new(®istry);
|
||||
vm.run(&get_context(function), &chunk)
|
||||
}
|
||||
|
||||
pub fn run_function(chunk: &AsmChunk, args: Vec<Value>, registry: Arc<AsmRegistry>) -> Result<Value, RuntimeError> {
|
||||
pub fn interpret_function(chunk: &AsmChunk, args: Vec<Value>, registry: Arc<AsmRegistry>) -> Result<Value, RuntimeError> {
|
||||
let mut vm = Vm::new(®istry);
|
||||
vm.run_function(chunk, args)
|
||||
}
|
||||
|
|
@ -241,7 +241,7 @@ impl Vm {
|
|||
return Err(RuntimeError::FunctionNotFound(function_name));
|
||||
}
|
||||
} else {
|
||||
let result = run_function(function_chunk.unwrap(), args, self.registry.clone())?;
|
||||
let result = interpret_function(function_chunk.unwrap(), args, self.registry.clone())?;
|
||||
self.push(Rc::new(RefCell::new(result)));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
"languages": [{
|
||||
"id": "tipi",
|
||||
"aliases": ["tipi-lang", "tipi"],
|
||||
"extensions": [".tp"],
|
||||
"extensions": [".tipi"],
|
||||
"configuration": "./language-configuration.json"
|
||||
}],
|
||||
"grammars": [{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue