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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
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]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
|
@ -250,7 +227,6 @@ dependencies = [
|
||||||
"iana-time-zone",
|
"iana-time-zone",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"serde",
|
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"windows-link 0.2.1",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
@ -328,9 +304,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.1.7"
|
version = "0.1.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
"typenum",
|
"typenum",
|
||||||
|
|
@ -516,9 +492,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.7"
|
version = "0.14.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
"version_check",
|
"version_check",
|
||||||
|
|
@ -959,13 +935,13 @@ checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "libredox"
|
||||||
version = "0.1.12"
|
version = "0.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
|
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.9.4",
|
"bitflags 2.9.4",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall 0.7.0",
|
"redox_syscall",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -1231,7 +1207,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall 0.5.18",
|
"redox_syscall",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"windows-link 0.2.1",
|
"windows-link 0.2.1",
|
||||||
]
|
]
|
||||||
|
|
@ -1279,26 +1255,6 @@ version = "0.3.32"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
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]]
|
[[package]]
|
||||||
name = "postgres-protocol"
|
name = "postgres-protocol"
|
||||||
version = "0.6.9"
|
version = "0.6.9"
|
||||||
|
|
@ -1324,7 +1280,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ef4605b7c057056dd35baeb6ac0c0338e4975b1f2bef0f65da953285eb007095"
|
checksum = "ef4605b7c057056dd35baeb6ac0c0338e4975b1f2bef0f65da953285eb007095"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"chrono",
|
|
||||||
"fallible-iterator",
|
"fallible-iterator",
|
||||||
"postgres-protocol",
|
"postgres-protocol",
|
||||||
]
|
]
|
||||||
|
|
@ -1409,15 +1364,6 @@ dependencies = [
|
||||||
"bitflags 2.9.4",
|
"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]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.12.2"
|
version = "1.12.2"
|
||||||
|
|
@ -1924,15 +1870,12 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arc-swap",
|
"arc-swap",
|
||||||
"axum",
|
"axum",
|
||||||
"bb8",
|
|
||||||
"bb8-postgres",
|
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"log",
|
"log",
|
||||||
"log4rs",
|
"log4rs",
|
||||||
"notify",
|
"notify",
|
||||||
"postgres",
|
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
@ -2204,18 +2147,18 @@ checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-normalization"
|
name = "unicode-normalization"
|
||||||
version = "0.1.25"
|
version = "0.1.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8"
|
checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-properties"
|
name = "unicode-properties"
|
||||||
version = "0.1.4"
|
version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d"
|
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-segmentation"
|
name = "unicode-segmentation"
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ log4rs = "1.4.0"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
tokio = { version = "1.47", features = ["full"] }
|
tokio = { version = "1.47", features = ["full"] }
|
||||||
tokio-postgres = "0.7"
|
tokio-postgres = "0.7"
|
||||||
chrono = { version= "0.4", features = ["serde"] }
|
chrono = "0.4.42"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
reqwest = { version = "0.12", features = ["json", "multipart"] }
|
reqwest = { version = "0.12", features = ["json", "multipart"] }
|
||||||
tower-http = { version = "0.6", features = ["fs"] }
|
tower-http = { version = "0.6", features = ["fs"] }
|
||||||
|
|
@ -26,6 +26,3 @@ clap = { version = "4.5.51", features = ["derive"] }
|
||||||
notify = "8.2.0"
|
notify = "8.2.0"
|
||||||
arc-swap = "1.7.1"
|
arc-swap = "1.7.1"
|
||||||
regex = "1.12.2"
|
regex = "1.12.2"
|
||||||
postgres = { version = "0.19", features = ["with-chrono-0_4"] }
|
|
||||||
bb8 = "0.9"
|
|
||||||
bb8-postgres = "0.9"
|
|
||||||
14
README.md
14
README.md
|
|
@ -1,8 +1,7 @@
|
||||||
# tipi-lang
|
# tipi-lang
|
||||||
|
|
||||||
<img src="icon.svg" width="400" alt="tipi"/>
|

|
||||||
|
Tipi/teepee means: 'the place where they live' in Sioux/Dakota.
|
||||||
Tipi/teepee means: 'the place where they live/dwell' in Sioux/Dakota.
|
|
||||||
see https://sesquiotic.com/2013/02/23/teepee/
|
see https://sesquiotic.com/2013/02/23/teepee/
|
||||||
|
|
||||||
Borrowing from that: 'the place where http lives'.
|
Borrowing from that: 'the place where http lives'.
|
||||||
|
|
@ -131,9 +130,8 @@ fn get() -> [Customer] | Customer? | ():
|
||||||
* test support
|
* test support
|
||||||
|
|
||||||
## What about performance?
|
## What about performance?
|
||||||
* Not optimizing for performance yet. First results look pretty bad.
|
* Clueless really! We'll see.
|
||||||
* as in: purely recursive fibonacci of 38 (the 38th fibonacci number) takes 42 seconds on my machine (in release mode) :sad-face.
|
* But it is written in rust
|
||||||
* That said the idea is that a lot of processing is done within the virtual machine.
|
|
||||||
* And it has no GC
|
* And it has no GC
|
||||||
* So, maybe it will compete with python?
|
* So, maybe it will compete with python?
|
||||||
|
|
||||||
|
|
@ -232,6 +230,4 @@ fn add(a: string, b: string) -> string:
|
||||||
|
|
||||||
ISSUES
|
ISSUES
|
||||||
* Make everything an expression. If is a statement and so it can not be type checked
|
* Make everything an expression. If is a statement and so it can not be type checked
|
||||||
* improve indenting
|
* improve indenting
|
||||||
|
|
||||||
WIP guards
|
|
||||||
|
|
@ -4,4 +4,4 @@ fn fib(n: u64):
|
||||||
else:
|
else:
|
||||||
fib(n-1) + fib(n-2)
|
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:
|
fn get(id: u32) -> Customer:
|
||||||
sql("select id, first_name, last_name from customers")
|
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:
|
fn get(id: u32) -> Customer:
|
||||||
db::get_all()
|
let customer = dao.get(id)
|
||||||
|
customer.date_fetched = current_date
|
||||||
|
|
||||||
|
fn add(customer: Customer):
|
||||||
|
dao.save(customer)
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,17 @@
|
||||||
fn get() -> list:
|
fn get():
|
||||||
service::get_all()
|
| 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')")
|
|
||||||
|
|
@ -2,4 +2,4 @@ object Person:
|
||||||
name: string
|
name: string
|
||||||
|
|
||||||
fn get(path: string) -> string:
|
fn get(path: string) -> string:
|
||||||
"hello " + path
|
"hello" + path
|
||||||
|
|
@ -1,41 +1,18 @@
|
||||||
use crate::DB_POOL;
|
use std::cell::{RefMut};
|
||||||
use crate::builtins::{FunctionMap, Signature, add};
|
use crate::builtins::{FunctionMap, Signature, add};
|
||||||
use crate::compiler::ast_pass::Parameter;
|
use crate::compiler::tokens::TokenType::{DateTime, StringType, Void};
|
||||||
use crate::compiler::tokens::TokenType;
|
|
||||||
use crate::compiler::tokens::TokenType::{DateTime, ListType, StringType, Void};
|
|
||||||
use crate::errors::RuntimeError;
|
use crate::errors::RuntimeError;
|
||||||
use crate::value::{Value, string};
|
use crate::value::Value;
|
||||||
use std::cell::RefMut;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
use tokio_postgres::types::Type;
|
use crate::compiler::ast_pass::Parameter;
|
||||||
|
|
||||||
pub(crate) static GLOBAL_FUNCTIONS: LazyLock<FunctionMap> = LazyLock::new(|| {
|
pub(crate) static GLOBAL_FUNCTIONS: LazyLock<FunctionMap> = LazyLock::new(|| {
|
||||||
let mut global_functions: FunctionMap = HashMap::new();
|
let mut global_functions: FunctionMap = HashMap::new();
|
||||||
let functions = &mut global_functions;
|
let functions = &mut global_functions;
|
||||||
add(functions, "now", Signature::new(vec![], DateTime, now));
|
add(functions, "now", Signature::new(vec![], DateTime, now));
|
||||||
add(
|
add(functions, "print", Signature::new(vec![Parameter::new("text", StringType)], Void, print));
|
||||||
functions,
|
add(functions, "println", Signature::new(vec![Parameter::new("text", StringType)], Void, println));
|
||||||
"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,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
global_functions
|
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> {
|
fn now(_self_val: RefMut<Value>, _args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||||
Ok(Value::DateTime(Box::new(chrono::Utc::now())))
|
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))
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,6 @@ pub(crate) fn list_functions() -> FunctionMap {
|
||||||
"remove",
|
"remove",
|
||||||
Signature::new(vec![Parameter::new("index", U64)], U64, remove),
|
Signature::new(vec![Parameter::new("index", U64)], U64, remove),
|
||||||
);
|
);
|
||||||
add(
|
|
||||||
functions,
|
|
||||||
"sort",
|
|
||||||
Signature::new(vec![], U64, sort),
|
|
||||||
);
|
|
||||||
list_functions
|
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 {
|
fn expected_a_list() -> RuntimeError {
|
||||||
expected("list")
|
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 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 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::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
use crate::compiler::ast_pass::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>,
|
||||||
|
|
@ -34,12 +32,8 @@ impl Signature {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn arity(&self) -> u8 {
|
pub(crate) fn arity(&self) -> usize {
|
||||||
self.parameters.len() as u8
|
self.parameters.len()
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn has_varargs(&self) -> bool {
|
|
||||||
!self.parameters.is_empty() && self.parameters.last().unwrap().name.lexeme == "varargs"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -53,7 +47,6 @@ static FUNCTIONS: LazyLock<FunctionTable> = LazyLock::new(|| {
|
||||||
let mut table: FunctionTable = HashMap::new();
|
let mut table: FunctionTable = HashMap::new();
|
||||||
table.insert("string".to_string(), string_functions());
|
table.insert("string".to_string(), string_functions());
|
||||||
table.insert("list".to_string(), list_functions());
|
table.insert("list".to_string(), list_functions());
|
||||||
table.insert("map".to_string(), map_functions());
|
|
||||||
|
|
||||||
table
|
table
|
||||||
});
|
});
|
||||||
|
|
@ -62,11 +55,8 @@ pub(crate) fn add(m: &mut FunctionMap, name: &str, method: Signature) {
|
||||||
m.insert(name.to_string(), method);
|
m.insert(name.to_string(), method);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn lookup(
|
pub(crate) fn lookup(type_name: &str, method_name: &str) -> Result<&'static Signature, CompilerError> {
|
||||||
type_name: &str,
|
FUNCTIONS
|
||||||
method_name: &str,
|
|
||||||
) -> Result<&'static Signature, CompilerError> {
|
|
||||||
FUNCTIONS
|
|
||||||
.get(type_name)
|
.get(type_name)
|
||||||
.and_then(|methods| methods.get(method_name))
|
.and_then(|methods| methods.get(method_name))
|
||||||
.ok_or_else(|| CompilerError::FunctionNotFound(format!("{}.{}", type_name, method_name)))
|
.ok_or_else(|| CompilerError::FunctionNotFound(format!("{}.{}", type_name, method_name)))
|
||||||
|
|
@ -78,9 +68,7 @@ pub(crate) fn call(
|
||||||
self_val: Rc<RefCell<Value>>,
|
self_val: Rc<RefCell<Value>>,
|
||||||
args: Vec<Value>,
|
args: Vec<Value>,
|
||||||
) -> Result<Value, RuntimeError> {
|
) -> Result<Value, RuntimeError> {
|
||||||
(lookup(type_name, method_name)
|
(lookup(type_name,method_name).map_err(|e|RuntimeError::FunctionNotFound(e.to_string()))?.function)(self_val.borrow_mut(), args)
|
||||||
.map_err(|e| RuntimeError::FunctionNotFound(e.to_string()))?
|
|
||||||
.function)(self_val.borrow_mut(), args)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn expected(expected_type: &str) -> RuntimeError {
|
pub(crate) fn expected(expected_type: &str) -> RuntimeError {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ use crate::value::Value;
|
||||||
use crate::{AsmRegistry, SymbolTable};
|
use crate::{AsmRegistry, SymbolTable};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use crate::compiler::namespace::Namespace;
|
|
||||||
|
|
||||||
pub fn compile(
|
pub fn compile(
|
||||||
qualified_name: Option<&str>,
|
qualified_name: Option<&str>,
|
||||||
|
|
@ -31,7 +30,7 @@ pub fn compile_function(
|
||||||
function: &Function,
|
function: &Function,
|
||||||
symbols: &SymbolTable,
|
symbols: &SymbolTable,
|
||||||
registry: &mut AsmRegistry,
|
registry: &mut AsmRegistry,
|
||||||
namespace: &Namespace,
|
namespace: &str,
|
||||||
) -> Result<AsmChunk, CompilerErrorAtLine> {
|
) -> Result<AsmChunk, CompilerErrorAtLine> {
|
||||||
let fn_name = &function.name.lexeme;
|
let fn_name = &function.name.lexeme;
|
||||||
let mut compiler = AsmPass::new(fn_name);
|
let mut compiler = AsmPass::new(fn_name);
|
||||||
|
|
@ -54,7 +53,7 @@ pub fn compile_in_namespace(
|
||||||
) -> Result<(), CompilerErrorAtLine> {
|
) -> Result<(), CompilerErrorAtLine> {
|
||||||
let name = namespace.unwrap_or("main");
|
let name = namespace.unwrap_or("main");
|
||||||
let mut compiler = AsmPass::new(name);
|
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);
|
registry.insert(name.to_string(), chunk);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -137,7 +136,7 @@ impl AsmPass {
|
||||||
ast: &Vec<Statement>,
|
ast: &Vec<Statement>,
|
||||||
symbols: &SymbolTable,
|
symbols: &SymbolTable,
|
||||||
registry: &mut AsmRegistry,
|
registry: &mut AsmRegistry,
|
||||||
namespace: &Namespace,
|
namespace: &str,
|
||||||
) -> Result<AsmChunk, CompilerErrorAtLine> {
|
) -> Result<AsmChunk, CompilerErrorAtLine> {
|
||||||
self.compile_statements(ast, symbols, registry, namespace)?;
|
self.compile_statements(ast, symbols, registry, namespace)?;
|
||||||
self.emit(Return);
|
self.emit(Return);
|
||||||
|
|
@ -152,7 +151,7 @@ impl AsmPass {
|
||||||
ast: &Vec<Statement>,
|
ast: &Vec<Statement>,
|
||||||
symbols: &SymbolTable,
|
symbols: &SymbolTable,
|
||||||
registry: &mut AsmRegistry,
|
registry: &mut AsmRegistry,
|
||||||
namespace: &Namespace,
|
namespace: &str,
|
||||||
) -> Result<(), CompilerErrorAtLine> {
|
) -> Result<(), CompilerErrorAtLine> {
|
||||||
for statement in ast {
|
for statement in ast {
|
||||||
self.compile_statement(statement, symbols, registry, namespace)?;
|
self.compile_statement(statement, symbols, registry, namespace)?;
|
||||||
|
|
@ -166,7 +165,7 @@ impl AsmPass {
|
||||||
statement: &Statement,
|
statement: &Statement,
|
||||||
symbols: &SymbolTable,
|
symbols: &SymbolTable,
|
||||||
registry: &mut AsmRegistry,
|
registry: &mut AsmRegistry,
|
||||||
namespace: &Namespace,
|
namespace: &str,
|
||||||
) -> Result<(), CompilerErrorAtLine> {
|
) -> Result<(), CompilerErrorAtLine> {
|
||||||
self.current_line = statement.line();
|
self.current_line = statement.line();
|
||||||
match statement {
|
match statement {
|
||||||
|
|
@ -194,7 +193,7 @@ impl AsmPass {
|
||||||
|
|
||||||
fn compile_expression(
|
fn compile_expression(
|
||||||
&mut self,
|
&mut self,
|
||||||
namespace: &Namespace,
|
namespace: &str,
|
||||||
expression: &Expression,
|
expression: &Expression,
|
||||||
symbols: &SymbolTable,
|
symbols: &SymbolTable,
|
||||||
registry: &mut AsmRegistry,
|
registry: &mut AsmRegistry,
|
||||||
|
|
@ -259,8 +258,8 @@ impl AsmPass {
|
||||||
Expression::FunctionCall {
|
Expression::FunctionCall {
|
||||||
name, arguments, ..
|
name, arguments, ..
|
||||||
} => {
|
} => {
|
||||||
let qname = format!("{}/{}", namespace.pop(), name.replace("::","/"));
|
let qname = format!("{}/{}", namespace, name);
|
||||||
let function = symbols.get(&qname);
|
let function = symbols.get(&qname).or(symbols.get(name));
|
||||||
match function {
|
match function {
|
||||||
Some(Symbol::Function { parameters, .. }) => {
|
Some(Symbol::Function { parameters, .. }) => {
|
||||||
let name_index = self.chunk.find_constant(&qname).unwrap_or_else(|| {
|
let name_index = self.chunk.find_constant(&qname).unwrap_or_else(|| {
|
||||||
|
|
@ -271,7 +270,7 @@ impl AsmPass {
|
||||||
namespace, symbols, registry, arguments, parameters,
|
namespace, symbols, registry, arguments, parameters,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
self.emit(Call(name_index, arguments.len() as u8));
|
self.emit(Call(name_index, arguments.len()));
|
||||||
}
|
}
|
||||||
// constructor function
|
// constructor function
|
||||||
Some(Symbol::Object { fields, .. }) => {
|
Some(Symbol::Object { fields, .. }) => {
|
||||||
|
|
@ -281,7 +280,7 @@ impl AsmPass {
|
||||||
self.get_arguments_in_order(
|
self.get_arguments_in_order(
|
||||||
namespace, symbols, registry, arguments, fields,
|
namespace, symbols, registry, arguments, fields,
|
||||||
)?;
|
)?;
|
||||||
self.emit(Call(name_index, arguments.len() as u8));
|
self.emit(Call(name_index, arguments.len()));
|
||||||
}
|
}
|
||||||
// maybe global function
|
// maybe global function
|
||||||
_ => {
|
_ => {
|
||||||
|
|
@ -296,12 +295,7 @@ impl AsmPass {
|
||||||
let name_index = self.chunk.find_constant(name).unwrap_or_else(|| {
|
let name_index = self.chunk.find_constant(name).unwrap_or_else(|| {
|
||||||
self.chunk.add_constant(Value::String(name.to_string()))
|
self.chunk.add_constant(Value::String(name.to_string()))
|
||||||
});
|
});
|
||||||
let arity = if fun.has_varargs() {
|
self.emit(Call(name_index, fun.arity()));
|
||||||
arguments.len() as u8
|
|
||||||
} else {
|
|
||||||
fun.arity()
|
|
||||||
};
|
|
||||||
self.emit(Call(name_index, arity));
|
|
||||||
} else {
|
} else {
|
||||||
return Err(self
|
return Err(self
|
||||||
.error_at_line(CompilerError::FunctionNotFound(name.to_string())));
|
.error_at_line(CompilerError::FunctionNotFound(name.to_string())));
|
||||||
|
|
@ -331,7 +325,7 @@ impl AsmPass {
|
||||||
});
|
});
|
||||||
let signature =
|
let signature =
|
||||||
lookup(&receiver_type, method_name).map_err(|e| self.error_at_line(e))?;
|
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(
|
return Err(self.error_at_line(CompilerError::IllegalArgumentsException(
|
||||||
format!("{}.{}", receiver_type, method_name),
|
format!("{}.{}", receiver_type, method_name),
|
||||||
signature.parameters.len(),
|
signature.parameters.len(),
|
||||||
|
|
@ -345,7 +339,7 @@ impl AsmPass {
|
||||||
arguments,
|
arguments,
|
||||||
&signature.parameters,
|
&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, .. } => {
|
Expression::Variable { name, .. } => {
|
||||||
let name_index = self.vars.get(name);
|
let name_index = self.vars.get(name);
|
||||||
|
|
@ -450,7 +444,11 @@ impl AsmPass {
|
||||||
}
|
}
|
||||||
Expression::MapGet { .. } => {}
|
Expression::MapGet { .. } => {}
|
||||||
Expression::FieldGet { .. } => {}
|
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 {
|
Expression::ForStatement {
|
||||||
loop_var,
|
loop_var,
|
||||||
range,
|
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
|
// 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(
|
fn get_arguments_in_order(
|
||||||
&mut self,
|
&mut self,
|
||||||
namespace: &Namespace,
|
namespace: &str,
|
||||||
symbols: &SymbolTable,
|
symbols: &SymbolTable,
|
||||||
registry: &mut AsmRegistry,
|
registry: &mut AsmRegistry,
|
||||||
arguments: &[Expression],
|
arguments: &[Expression],
|
||||||
|
|
@ -552,7 +550,7 @@ pub enum Op {
|
||||||
Divide,
|
Divide,
|
||||||
Negate,
|
Negate,
|
||||||
Return,
|
Return,
|
||||||
Call(usize, u8),
|
Call(usize, usize),
|
||||||
And,
|
And,
|
||||||
Or,
|
Or,
|
||||||
Not,
|
Not,
|
||||||
|
|
@ -573,7 +571,7 @@ pub enum Op {
|
||||||
DefMap(usize),
|
DefMap(usize),
|
||||||
Assign(usize),
|
Assign(usize),
|
||||||
ListGet,
|
ListGet,
|
||||||
CallBuiltin(usize, usize, u8),
|
CallBuiltin(usize, usize, usize),
|
||||||
Dup,
|
Dup,
|
||||||
GotoIf(usize),
|
GotoIf(usize),
|
||||||
GotoIfNot(usize),
|
GotoIfNot(usize),
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
use crate::builtins::globals::GLOBAL_FUNCTIONS;
|
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::{
|
use crate::compiler::tokens::TokenType::{
|
||||||
Bang, Bool, Char, Colon, DateTime, Dot, Else, Eof, Eol, Equal, False, FloatingPoint, Fn, For,
|
Bang, Bool, Char, Colon, DateTime, Dot, Else, Eof, Eol, Equal, False, FloatingPoint, Fn, For,
|
||||||
Greater, GreaterEqual, GreaterGreater, Identifier, If, In, Indent, Integer, LeftBrace,
|
Greater, GreaterEqual, GreaterGreater, Identifier, If, In, Indent, Integer, LeftBrace,
|
||||||
|
|
@ -147,7 +151,7 @@ impl AstCompiler {
|
||||||
Err(self.error_at_line(Expected("-> or ?")))
|
Err(self.error_at_line(Expected("-> or ?")))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Ok(Expression::Stop {
|
Ok(Stop {
|
||||||
line: self.peek().line,
|
line: self.peek().line,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -158,7 +162,7 @@ impl AstCompiler {
|
||||||
self.consume(&RightBrace, Expected("'}' after guard expression."))?;
|
self.consume(&RightBrace, Expected("'}' after guard expression."))?;
|
||||||
Ok(query_params)
|
Ok(query_params)
|
||||||
} else {
|
} else {
|
||||||
Ok(Expression::Stop {
|
Ok(Stop {
|
||||||
line: self.peek().line,
|
line: self.peek().line,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -170,7 +174,7 @@ impl AstCompiler {
|
||||||
self.consume(&RightBrace, Expected("'}' after guard expression."))?;
|
self.consume(&RightBrace, Expected("'}' after guard expression."))?;
|
||||||
Ok(path_params)
|
Ok(path_params)
|
||||||
} else {
|
} else {
|
||||||
Ok(Expression::Stop {
|
Ok(Stop {
|
||||||
line: self.peek().line,
|
line: self.peek().line,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -343,7 +347,7 @@ impl AstCompiler {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Expression::LetExpression {
|
Ok(LetExpression {
|
||||||
name: name_token,
|
name: name_token,
|
||||||
var_type,
|
var_type,
|
||||||
initializer: Box::new(initializer),
|
initializer: Box::new(initializer),
|
||||||
|
|
@ -387,8 +391,8 @@ impl AstCompiler {
|
||||||
if self.match_token(&[Equal]) {
|
if self.match_token(&[Equal]) {
|
||||||
let operator = self.previous().clone();
|
let operator = self.previous().clone();
|
||||||
let right = self.comparison(symbol_table)?;
|
let right = self.comparison(symbol_table)?;
|
||||||
if let Expression::Variable { name, .. } = expr {
|
if let Variable { name, .. } = expr {
|
||||||
Ok(Expression::Assignment {
|
Ok(Assignment {
|
||||||
line: operator.line,
|
line: operator.line,
|
||||||
variable_name: name.to_string(),
|
variable_name: name.to_string(),
|
||||||
value: Box::new(right),
|
value: Box::new(right),
|
||||||
|
|
@ -495,13 +499,13 @@ impl AstCompiler {
|
||||||
|
|
||||||
self.inc_indent();
|
self.inc_indent();
|
||||||
|
|
||||||
Ok(Expression::IfElseExpression {
|
Ok(IfElseExpression {
|
||||||
condition: Box::new(condition),
|
condition: Box::new(condition),
|
||||||
then_branch,
|
then_branch,
|
||||||
else_branch: self.compile(symbol_table)?,
|
else_branch: self.compile(symbol_table)?,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Ok(Expression::IfExpression {
|
Ok(IfExpression {
|
||||||
condition: Box::new(condition),
|
condition: Box::new(condition),
|
||||||
then_branch,
|
then_branch,
|
||||||
})
|
})
|
||||||
|
|
@ -530,23 +534,22 @@ impl AstCompiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// index into list
|
|
||||||
fn index(&mut self, operand: Expression, index: Expression) -> Expr {
|
fn index(&mut self, operand: Expression, index: Expression) -> Expr {
|
||||||
let get = match &operand {
|
let get = match &operand {
|
||||||
Expression::Map { .. } => Expression::MapGet {
|
Expression::Map { .. } => MapGet {
|
||||||
map: Box::new(operand),
|
map: Box::new(operand),
|
||||||
key: Box::new(index),
|
key: Box::new(index),
|
||||||
},
|
},
|
||||||
Expression::List { .. } => Expression::ListGet {
|
Expression::List { .. } => ListGet {
|
||||||
list: Box::new(operand),
|
list: Box::new(operand),
|
||||||
index: Box::new(index),
|
index: Box::new(index),
|
||||||
},
|
},
|
||||||
Expression::Variable { var_type, .. } => match var_type {
|
Variable { var_type, .. } => match var_type {
|
||||||
ListType => Expression::ListGet {
|
ListType => ListGet {
|
||||||
list: Box::new(operand),
|
list: Box::new(operand),
|
||||||
index: Box::new(index),
|
index: Box::new(index),
|
||||||
},
|
},
|
||||||
MapType => Expression::MapGet {
|
MapType => MapGet {
|
||||||
map: Box::new(operand),
|
map: Box::new(operand),
|
||||||
key: Box::new(index),
|
key: Box::new(index),
|
||||||
},
|
},
|
||||||
|
|
@ -575,7 +578,7 @@ impl AstCompiler {
|
||||||
) -> Expr {
|
) -> Expr {
|
||||||
if self.match_token(&[LeftParen]) {
|
if self.match_token(&[LeftParen]) {
|
||||||
let arguments = self.arguments(symbol_table)?;
|
let arguments = self.arguments(symbol_table)?;
|
||||||
Ok(Expression::MethodCall {
|
Ok(MethodCall {
|
||||||
receiver: Box::new(receiver.clone()),
|
receiver: Box::new(receiver.clone()),
|
||||||
method_name: op.lexeme,
|
method_name: op.lexeme,
|
||||||
arguments,
|
arguments,
|
||||||
|
|
@ -583,7 +586,7 @@ impl AstCompiler {
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// no test yet
|
// no test yet
|
||||||
Ok(Expression::FieldGet {
|
Ok(FieldGet {
|
||||||
receiver: Box::new(receiver.clone()),
|
receiver: Box::new(receiver.clone()),
|
||||||
field: op.lexeme.clone(),
|
field: op.lexeme.clone(),
|
||||||
})
|
})
|
||||||
|
|
@ -695,7 +698,7 @@ impl AstCompiler {
|
||||||
fn named_parameter(&mut self, name: &Token, symbol_table: &mut SymbolTable) -> Expr {
|
fn named_parameter(&mut self, name: &Token, symbol_table: &mut SymbolTable) -> Expr {
|
||||||
let value = self.expression(symbol_table)?;
|
let value = self.expression(symbol_table)?;
|
||||||
let line = name.line;
|
let line = name.line;
|
||||||
Ok(Expression::NamedParameter {
|
Ok(NamedParameter {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
value: Box::new(value),
|
value: Box::new(value),
|
||||||
line,
|
line,
|
||||||
|
|
@ -748,7 +751,7 @@ impl AstCompiler {
|
||||||
} else {
|
} else {
|
||||||
&Unknown
|
&Unknown
|
||||||
};
|
};
|
||||||
Ok(Expression::Variable {
|
Ok(Variable {
|
||||||
name: name.lexeme.to_string(),
|
name: name.lexeme.to_string(),
|
||||||
var_type: var_type.clone(),
|
var_type: var_type.clone(),
|
||||||
line: name.line,
|
line: name.line,
|
||||||
|
|
@ -757,7 +760,7 @@ impl AstCompiler {
|
||||||
|
|
||||||
fn function_call(&mut self, name: Token, symbol_table: &mut SymbolTable) -> Expr {
|
fn function_call(&mut self, name: Token, symbol_table: &mut SymbolTable) -> Expr {
|
||||||
let arguments = self.arguments(symbol_table)?;
|
let arguments = self.arguments(symbol_table)?;
|
||||||
Ok(Expression::FunctionCall {
|
Ok(FunctionCall {
|
||||||
line: self.peek().line,
|
line: self.peek().line,
|
||||||
name: name.lexeme.to_string(),
|
name: name.lexeme.to_string(),
|
||||||
arguments,
|
arguments,
|
||||||
|
|
@ -888,24 +891,13 @@ impl Parameter {
|
||||||
pub fn new(name: impl Into<String>, value_type: TokenType) -> Self {
|
pub fn new(name: impl Into<String>, value_type: TokenType) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: Token {
|
name: Token {
|
||||||
token_type: StringType,
|
token_type: TokenType::StringType,
|
||||||
lexeme: name.into(),
|
lexeme: name.into(),
|
||||||
line: 0,
|
line: 0,
|
||||||
},
|
},
|
||||||
var_type: value_type,
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -1017,19 +1009,19 @@ impl Expression {
|
||||||
Self::Range { line, .. } => *line,
|
Self::Range { line, .. } => *line,
|
||||||
Self::List { line, .. } => *line,
|
Self::List { line, .. } => *line,
|
||||||
Self::Map { line, .. } => *line,
|
Self::Map { line, .. } => *line,
|
||||||
Self::Variable { line, .. } => *line,
|
Variable { line, .. } => *line,
|
||||||
Self::Assignment { line, .. } => *line,
|
Assignment { line, .. } => *line,
|
||||||
Self::FunctionCall { line, .. } => *line,
|
FunctionCall { line, .. } => *line,
|
||||||
Self::MethodCall { line, .. } => *line,
|
MethodCall { line, .. } => *line,
|
||||||
Self::Stop { line } => *line,
|
Stop { line } => *line,
|
||||||
Self::NamedParameter { line, .. } => *line,
|
NamedParameter { line, .. } => *line,
|
||||||
Self::MapGet { .. } => 0,
|
MapGet { .. } => 0,
|
||||||
Self::ListGet { .. } => 0,
|
ListGet { .. } => 0,
|
||||||
Self::FieldGet { .. } => 0,
|
FieldGet { .. } => 0,
|
||||||
Self::IfExpression { condition, .. } => condition.line(),
|
IfExpression { condition, .. } => condition.line(),
|
||||||
Self::IfElseExpression { condition, .. } => condition.line(),
|
IfElseExpression { condition, .. } => condition.line(),
|
||||||
Self::LetExpression { name, .. } => name.line,
|
LetExpression { name, .. } => name.line,
|
||||||
Self::ForStatement { loop_var, .. } => loop_var.line,
|
Expression::ForStatement { loop_var, .. } => loop_var.line,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::DATE_FORMAT_TIMEZONE;
|
use crate::DATE_FORMAT_TIMEZONE;
|
||||||
use crate::compiler::{compile, run};
|
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::CompilerErrorAtLine;
|
||||||
use crate::errors::RuntimeError::{IllegalArgumentException, IndexOutOfBounds, NotSortable};
|
use crate::errors::RuntimeError::{IllegalArgumentException, IndexOutOfBounds};
|
||||||
use crate::errors::TipiLangError::{Compiler, Runtime};
|
use crate::errors::TipiLangError::{Compiler, Runtime};
|
||||||
use crate::value::{Value, i64, string};
|
use crate::value::{Value, string};
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -299,28 +299,6 @@ date"#),
|
||||||
assert_eq!(run(r#"[1,2,3].len()"#), Ok(Value::U64(3)));
|
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]
|
#[test]
|
||||||
fn list_push() {
|
fn list_push() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
@ -471,14 +449,12 @@ let a:i64 = if true:
|
||||||
42
|
42
|
||||||
else:
|
else:
|
||||||
0
|
0
|
||||||
a"#)
|
a"#).unwrap();
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn package() {
|
|
||||||
assert_eq!(run(r#"a::b.c()"#), Err(Compiler(CompilerErrorAtLine {error: UndeclaredVariable("a::b".to_string()), line: 1 })));
|
|
||||||
}
|
}
|
||||||
|
// #[test]
|
||||||
|
// fn package() {
|
||||||
|
// assert_eq!(run(r#"a.b.c()"#), Ok(Value::U32(48)));
|
||||||
|
// }
|
||||||
|
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn guards() {
|
// fn guards() {
|
||||||
|
|
@ -488,19 +464,4 @@ a"#)
|
||||||
// Ok(Value::Void)
|
// 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::collections::HashMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use log::info;
|
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
use crate::{symbol_builder, AsmRegistry, TIPI_EXT};
|
use crate::{symbol_builder, AsmRegistry, TIPI_EXT};
|
||||||
use crate::compiler::assembly_pass::AsmChunk;
|
use crate::compiler::assembly_pass::AsmChunk;
|
||||||
|
|
@ -12,18 +11,17 @@ pub mod scan_pass;
|
||||||
pub mod ast_pass;
|
pub mod ast_pass;
|
||||||
pub mod tokens;
|
pub mod tokens;
|
||||||
pub mod assembly_pass;
|
pub mod assembly_pass;
|
||||||
pub(crate) mod namespace;
|
|
||||||
|
|
||||||
pub fn compile_sourcedir(source_dir: &str) -> Result<HashMap<String, AsmChunk>, TipiLangError> {
|
pub fn compile_sourcedir(source_dir: &str) -> Result<HashMap<String, AsmChunk>, TipiLangError> {
|
||||||
let mut asm_registry = AsmRegistry::new();
|
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()) {
|
for entry in WalkDir::new(source_dir).into_iter().filter_map(|e| e.ok()) {
|
||||||
let path = entry.path().to_str().unwrap();
|
let path = entry.path().to_str().unwrap();
|
||||||
if path.ends_with(TIPI_EXT) {
|
if path.ends_with(TIPI_EXT) {
|
||||||
info!("-- Compiling {} -- ", path);
|
print!("-- Compiling {} -- ", path);
|
||||||
let source = fs::read_to_string(path).map_err(map_underlying())?;
|
let source = fs::read_to_string(path).map_err(map_underlying())?;
|
||||||
let tokens = scan_pass::scan(&source)?;
|
let tokens = scan_pass::scan(&source)?;
|
||||||
|
let mut symbol_table = HashMap::new();
|
||||||
match ast_pass::compile(Some(path), tokens, &mut symbol_table) {
|
match ast_pass::compile(Some(path), tokens, &mut symbol_table) {
|
||||||
Ok(statements) => {
|
Ok(statements) => {
|
||||||
let path = path.strip_prefix(source_dir).unwrap().replace(TIPI_EXT, "");
|
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();
|
let mut asm_registry = HashMap::new();
|
||||||
assembly_pass::compile(None, &ast, &symbol_table, &mut asm_registry)?;
|
assembly_pass::compile(None, &ast, &symbol_table, &mut asm_registry)?;
|
||||||
let registry = arc_swap::ArcSwap::from(std::sync::Arc::new(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::Hash),
|
||||||
'+' => self.add_token(TokenType::Plus),
|
'+' => self.add_token(TokenType::Plus),
|
||||||
':' => {
|
':' => self.add_token(TokenType::Colon),
|
||||||
if !self.match_next(':') {
|
|
||||||
self.add_token(TokenType::Colon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
';' => println!("Warning: Ignoring semicolon at line {}", self.line),
|
';' => println!("Warning: Ignoring semicolon at line {}", self.line),
|
||||||
'*' => self.add_token(TokenType::Star),
|
'*' => self.add_token(TokenType::Star),
|
||||||
'!' => {
|
'!' => {
|
||||||
|
|
@ -186,13 +182,7 @@ impl Scanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn identifier(&mut self) {
|
fn identifier(&mut self) {
|
||||||
while is_alphanumeric(self.peek())
|
while is_alphanumeric(self.peek()) {
|
||||||
|| self.peek() == '_'
|
|
||||||
|| (self.peek() == ':' && self.peek_next() == ':')
|
|
||||||
{
|
|
||||||
if self.peek() == ':' && self.peek_next() == ':' {
|
|
||||||
self.advance();
|
|
||||||
}
|
|
||||||
self.advance();
|
self.advance();
|
||||||
}
|
}
|
||||||
let value: String = self.chars[self.start..self.current].iter().collect();
|
let value: String = self.chars[self.start..self.current].iter().collect();
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ pub enum TokenType {
|
||||||
Bool,
|
Bool,
|
||||||
Char,
|
Char,
|
||||||
Colon,
|
Colon,
|
||||||
ColonColon,
|
|
||||||
Comma,
|
Comma,
|
||||||
DateTime,
|
DateTime,
|
||||||
Dot,
|
Dot,
|
||||||
|
|
@ -114,7 +113,6 @@ impl fmt::Display for TokenType {
|
||||||
TokenType::Pipe => write!(f, "|"),
|
TokenType::Pipe => write!(f, "|"),
|
||||||
TokenType::BitXor => write!(f, "^"),
|
TokenType::BitXor => write!(f, "^"),
|
||||||
TokenType::Colon => write!(f, ":"),
|
TokenType::Colon => write!(f, ":"),
|
||||||
TokenType::ColonColon => write!(f, "::"),
|
|
||||||
TokenType::Comma => write!(f, ","),
|
TokenType::Comma => write!(f, ","),
|
||||||
TokenType::FloatingPoint => write!(f, "float"),
|
TokenType::FloatingPoint => write!(f, "float"),
|
||||||
TokenType::MapType => write!(f, "map"),
|
TokenType::MapType => write!(f, "map"),
|
||||||
|
|
|
||||||
|
|
@ -96,8 +96,6 @@ pub enum RuntimeError {
|
||||||
ExpectedType(String),
|
ExpectedType(String),
|
||||||
#[error("Index out of bounds: {0} > {1}")]
|
#[error("Index out of bounds: {0} > {1}")]
|
||||||
IndexOutOfBounds(usize, usize),
|
IndexOutOfBounds(usize, usize),
|
||||||
#[error("The list contains values that are not comparable.")]
|
|
||||||
NotSortable,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug, PartialEq)]
|
#[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 s = source.to_string();
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
info!("-- File watch started --");
|
println!("-- File watch started --");
|
||||||
let path = Path::new(&source);
|
let path = Path::new(&source);
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
panic!("source directory {} does not exist", &source);
|
panic!("source directory {} does not exist", &source);
|
||||||
}
|
}
|
||||||
let mut watcher = notify::recommended_watcher(tx)
|
let mut watcher = notify::recommended_watcher(tx).expect("Failed to create watcher");
|
||||||
.expect("Failed to create watcher");
|
|
||||||
watcher
|
watcher
|
||||||
.watch(path, RecursiveMode::Recursive)
|
.watch(path, RecursiveMode::Recursive)
|
||||||
.expect("Failed to watch");
|
.expect("Failed to watch");
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,6 @@ use crate::compiler::ast_pass::{Expression, Statement};
|
||||||
use crate::errors::CompilerErrorAtLine;
|
use crate::errors::CompilerErrorAtLine;
|
||||||
use crate::symbol_builder::Symbol;
|
use crate::symbol_builder::Symbol;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::OnceLock;
|
|
||||||
use bb8_postgres::PostgresConnectionManager;
|
|
||||||
use tokio_postgres::NoTls;
|
|
||||||
|
|
||||||
mod builtins;
|
mod builtins;
|
||||||
pub mod compiler;
|
pub mod compiler;
|
||||||
|
|
@ -14,7 +11,7 @@ pub mod file_watch;
|
||||||
mod keywords;
|
mod keywords;
|
||||||
pub mod repl;
|
pub mod repl;
|
||||||
mod symbol_builder;
|
mod symbol_builder;
|
||||||
pub mod value;
|
mod value;
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
|
|
||||||
pub(crate) type SymbolTable = HashMap<String, Symbol>;
|
pub(crate) type SymbolTable = HashMap<String, Symbol>;
|
||||||
|
|
@ -23,6 +20,4 @@ pub(crate) type Stmt = Result<Statement, CompilerErrorAtLine>;
|
||||||
pub(crate) type AsmRegistry = HashMap<String, AsmChunk>;
|
pub(crate) type AsmRegistry = HashMap<String, AsmChunk>;
|
||||||
|
|
||||||
pub const TIPI_EXT: &str = ".tp";
|
pub const TIPI_EXT: &str = ".tp";
|
||||||
pub const DATE_FORMAT_TIMEZONE: &str = "%Y-%m-%d %H:%M:%S%.3f %z";
|
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::http::StatusCode;
|
||||||
use axum::routing::any;
|
use axum::routing::any;
|
||||||
use axum::{Json, Router};
|
use axum::{Json, Router};
|
||||||
use bb8_postgres::PostgresConnectionManager;
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use log::{error, info};
|
use log::info;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::sync::{Arc, LazyLock, OnceLock};
|
use std::sync::Arc;
|
||||||
use tipi_lang::compiler::assembly_pass::AsmChunk;
|
use tipi_lang::compiler::assembly_pass::AsmChunk;
|
||||||
use tipi_lang::compiler::{compile_sourcedir, map_underlying, run};
|
use tipi_lang::compiler::{compile_sourcedir, map_underlying, run};
|
||||||
use tipi_lang::errors::TipiLangError;
|
use tipi_lang::errors::TipiLangError;
|
||||||
use tipi_lang::value::Value;
|
use tipi_lang::vm::interpret_async;
|
||||||
use tipi_lang::vm::run_async;
|
|
||||||
use tipi_lang::{DB_POOL, vm};
|
|
||||||
use tokio_postgres::NoTls;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
/// A simple CLI tool to greet users
|
/// A simple CLI tool to greet users
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
|
|
@ -32,46 +27,27 @@ struct Args {
|
||||||
|
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
file: Option<String>,
|
file: Option<String>,
|
||||||
|
|
||||||
#[arg(short, long)]
|
|
||||||
postgres: Option<String>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), TipiLangError> {
|
async fn main() -> Result<(), TipiLangError> {
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
let args = Args::parse();
|
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 {
|
if let Some(file) = args.file {
|
||||||
let source = fs::read_to_string(file).expect("Unable to read file");
|
let source = fs::read_to_string(file).expect("Unable to read file");
|
||||||
run(&source)?;
|
run(&source)?;
|
||||||
} else {
|
} else {
|
||||||
info!("-- Tipilang --");
|
println!("-- Tipilang --");
|
||||||
let source = args.source.unwrap_or("./source".to_string());
|
let source = args.source.unwrap_or("./source".to_string());
|
||||||
let registry = compile_sourcedir(&source)?;
|
let registry = compile_sourcedir(&source)?;
|
||||||
let empty = registry.is_empty();
|
let empty = registry.is_empty();
|
||||||
|
|
||||||
let swap = Arc::new(ArcSwap::from(Arc::new(registry)));
|
let swap = Arc::new(ArcSwap::from(Arc::new(registry)));
|
||||||
if !empty {
|
if !empty {
|
||||||
let registry = swap.clone().load();
|
|
||||||
if registry.get("/main").is_some() {
|
|
||||||
vm::run(registry, "/main")?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if args.watch {
|
if args.watch {
|
||||||
tipi_lang::file_watch::start_watch_daemon(&source, swap.clone());
|
tipi_lang::file_watch::start_watch_daemon(&source, swap.clone());
|
||||||
}
|
}
|
||||||
info!("-- Compilation successful --");
|
println!("-- Compilation successful --");
|
||||||
let state = AppState {
|
let state = AppState {
|
||||||
registry: swap.clone(),
|
registry: swap.clone(),
|
||||||
};
|
};
|
||||||
|
|
@ -83,7 +59,7 @@ async fn main() -> Result<(), TipiLangError> {
|
||||||
.await
|
.await
|
||||||
.map_err(map_underlying())?;
|
.map_err(map_underlying())?;
|
||||||
|
|
||||||
info!(
|
println!(
|
||||||
"-- Listening on {} --\n",
|
"-- Listening on {} --\n",
|
||||||
listener.local_addr().map_err(map_underlying())?
|
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())?;
|
axum::serve(listener, app).await.map_err(map_underlying())?;
|
||||||
} else {
|
} else {
|
||||||
error!("No source files found or compilation error");
|
println!("No source files found or compilation error");
|
||||||
if args.repl {
|
if args.repl {
|
||||||
tipi_lang::repl::start(swap.clone())?;
|
tipi_lang::repl::start(swap.clone())?;
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +87,7 @@ struct AppState {
|
||||||
async fn handle_any(
|
async fn handle_any(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
req: Request,
|
req: Request,
|
||||||
) -> Result<Json<Value>, StatusCode> {
|
) -> Result<Json<String>, StatusCode> {
|
||||||
let method = req.method().to_string().to_ascii_lowercase();
|
let method = req.method().to_string().to_ascii_lowercase();
|
||||||
let uri = req.uri();
|
let uri = req.uri();
|
||||||
|
|
||||||
|
|
@ -133,7 +109,7 @@ async fn handle_any(
|
||||||
}
|
}
|
||||||
let path = &req.uri().to_string();
|
let path = &req.uri().to_string();
|
||||||
info!("invoked {:?} => {}", req, function_qname);
|
info!("invoked {:?} => {}", req, function_qname);
|
||||||
match run_async(
|
match interpret_async(
|
||||||
state.registry.load(),
|
state.registry.load(),
|
||||||
&function_qname,
|
&function_qname,
|
||||||
path,
|
path,
|
||||||
|
|
@ -142,7 +118,7 @@ async fn handle_any(
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(value) => Ok(Json(value)),
|
Ok(value) => Ok(Json(value.to_string())),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// url checks out but function for method not found
|
// url checks out but function for method not found
|
||||||
if state
|
if state
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,6 @@ use std::io;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use crate::compiler::namespace::Namespace;
|
|
||||||
|
|
||||||
pub fn start(registry: Arc<ArcSwap<HashMap<String, AsmChunk>>>) -> Result<(), TipiLangError> {
|
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)");
|
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);
|
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) => {
|
Ok(chunk) => {
|
||||||
registry_copy.insert("main".to_string(), chunk);
|
registry_copy.insert("main".to_string(), chunk);
|
||||||
registry.store(Arc::new(registry_copy));
|
registry.store(Arc::new(registry_copy));
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ fn make_qname(path: &str, name: &Token) -> String {
|
||||||
if path.is_empty() {
|
if path.is_empty() {
|
||||||
name.lexeme.to_string()
|
name.lexeme.to_string()
|
||||||
} else {
|
} 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::DATE_FORMAT_TIMEZONE;
|
||||||
use crate::errors::ValueError;
|
use crate::errors::ValueError;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::ser::{SerializeMap, SerializeSeq};
|
|
||||||
use serde::{Deserialize, Serialize, Serializer};
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Shl, Shr, Sub};
|
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 struct Object {
|
||||||
pub(crate) definition: String,
|
pub(crate) definition: String,
|
||||||
pub(crate) fields: Vec<(String, Value)>,
|
pub(crate) fields: Vec<(String, Value)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
U32(u32),
|
U32(u32),
|
||||||
I32(i32),
|
I32(i32),
|
||||||
|
|
@ -33,41 +31,13 @@ pub enum Value {
|
||||||
ObjectType(Box<Object>),
|
ObjectType(Box<Object>),
|
||||||
Error(String),
|
Error(String),
|
||||||
Void,
|
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 {
|
pub(crate) fn string(v: impl Into<String>) -> Value {
|
||||||
Value::String(v.into())
|
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())
|
Value::I64(v.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -139,24 +109,6 @@ impl Value {
|
||||||
_ => Err(ValueError::IllegalCast),
|
_ => 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 {
|
impl From<i32> for Value {
|
||||||
|
|
@ -247,7 +199,6 @@ impl Display for Value {
|
||||||
Value::Map(map) => to_string(f, map),
|
Value::Map(map) => to_string(f, map),
|
||||||
Value::Error(v) => write!(f, "{}", v),
|
Value::Error(v) => write!(f, "{}", v),
|
||||||
Value::Void => write!(f, "()"),
|
Value::Void => write!(f, "()"),
|
||||||
Value::Uuid(v) => write!(f, "{}", v),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub async fn run_async(
|
pub async fn interpret_async(
|
||||||
registry: Guard<Arc<HashMap<String, AsmChunk>>>,
|
registry: Guard<Arc<HashMap<String, AsmChunk>>>,
|
||||||
function: &str,
|
function: &str,
|
||||||
uri: &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 chunk = registry.get(function).unwrap().clone();
|
||||||
let mut vm = Vm::new(®istry);
|
let mut vm = Vm::new(®istry);
|
||||||
vm.run(&get_context(function), &chunk)
|
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);
|
let mut vm = Vm::new(®istry);
|
||||||
vm.run_function(chunk, args)
|
vm.run_function(chunk, args)
|
||||||
}
|
}
|
||||||
|
|
@ -241,7 +241,7 @@ impl Vm {
|
||||||
return Err(RuntimeError::FunctionNotFound(function_name));
|
return Err(RuntimeError::FunctionNotFound(function_name));
|
||||||
}
|
}
|
||||||
} else {
|
} 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)));
|
self.push(Rc::new(RefCell::new(result)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
"languages": [{
|
"languages": [{
|
||||||
"id": "tipi",
|
"id": "tipi",
|
||||||
"aliases": ["tipi-lang", "tipi"],
|
"aliases": ["tipi-lang", "tipi"],
|
||||||
"extensions": [".tp"],
|
"extensions": [".tipi"],
|
||||||
"configuration": "./language-configuration.json"
|
"configuration": "./language-configuration.json"
|
||||||
}],
|
}],
|
||||||
"grammars": [{
|
"grammars": [{
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue