aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock119
-rw-r--r--README.md32
-rw-r--r--src/compiler.rs92
-rw-r--r--src/lib.rs924
-rw-r--r--src/vm.rs26
-rw-r--r--sylt_macro/src/lib.rs2
6 files changed, 628 insertions, 567 deletions
diff --git a/Cargo.lock b/Cargo.lock
index fde14d2..f7d2ec1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -43,9 +43,9 @@ dependencies = [
[[package]]
name = "bumpalo"
-version = "3.4.0"
+version = "3.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e8c087f005730276d1096a652e92a8bacee2e2472bcc9715a74d2bec38b5820"
+checksum = "099e596ef14349721d9016f6b80dd3419ea1bf289ab9b44df8e4dfd3a005d5d9"
[[package]]
name = "byteorder"
@@ -64,12 +64,6 @@ dependencies = [
[[package]]
name = "cfg-if"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
-
-[[package]]
-name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
@@ -93,16 +87,16 @@ checksum = "28b9d6de7f49e22cf97ad17fc4036ece69300032f45f78f30b4a4482cdc3f4a6"
[[package]]
name = "criterion"
-version = "0.3.3"
+version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70daa7ceec6cf143990669a04c7df13391d55fb27bd4079d252fca774ba244d8"
+checksum = "ab327ed7354547cc2ef43cbe20ef68b988e70b4b593cbd66a2a61733123a3d23"
dependencies = [
"atty",
"cast",
"clap",
"criterion-plot",
"csv",
- "itertools",
+ "itertools 0.10.0",
"lazy_static",
"num-traits",
"oorandom",
@@ -124,7 +118,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e022feadec601fba1649cfa83586381a4ad31c6bf3a9ab7d408118b05dd9889d"
dependencies = [
"cast",
- "itertools",
+ "itertools 0.9.0",
]
[[package]]
@@ -133,7 +127,7 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"crossbeam-utils",
]
@@ -143,7 +137,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
@@ -154,7 +148,7 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"const_fn",
"crossbeam-utils",
"lazy_static",
@@ -169,7 +163,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
dependencies = [
"autocfg",
- "cfg-if 1.0.0",
+ "cfg-if",
"lazy_static",
]
@@ -209,15 +203,15 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "half"
-version = "1.6.0"
+version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177"
+checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3"
[[package]]
name = "hermit-abi"
-version = "0.1.17"
+version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8"
+checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
dependencies = [
"libc",
]
@@ -232,6 +226,15 @@ dependencies = [
]
[[package]]
+name = "itertools"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "37d572918e350e82412fe766d24b15e6682fb2ed2bbe018280caa810397cb319"
+dependencies = [
+ "either",
+]
+
+[[package]]
name = "itoa"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -239,9 +242,9 @@ checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
[[package]]
name = "js-sys"
-version = "0.3.46"
+version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175"
+checksum = "5cfb73131c35423a367daf8cbd24100af0d077668c8c2943f0e7dd775fef0f65"
dependencies = [
"wasm-bindgen",
]
@@ -254,17 +257,17 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
-version = "0.2.82"
+version = "0.2.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89203f3fba0a3795506acaad8ebce3c80c0af93f994d5a1d7a0b1eeb23271929"
+checksum = "7ccac4b00700875e6a07c6cde370d44d32fa01c5a65cdd2fca6858c479d28bb3"
[[package]]
name = "log"
-version = "0.4.13"
+version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fcf3805d4480bb5b86070dcfeb9e2cb2ebc148adb753c5cca5f884d1d65a42b2"
+checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
- "cfg-if 0.1.10",
+ "cfg-if",
]
[[package]]
@@ -334,21 +337,37 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "owo-colors"
version = "1.2.1"
-source = "git+https://github.com/FredTheDino/owo-colors.git#4ca8cc3da368d17831af67b85fa33215deb4570f"
+source = "git+https://github.com/FredTheDino/owo-colors.git#ccb9cf0cd41e21d1c7fa902f1475b84a5bfc01c8"
[[package]]
name = "plotters"
-version = "0.2.15"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0d1685fbe7beba33de0330629da9d955ac75bd54f33d7b79f9a895590124f6bb"
+checksum = "45ca0ae5f169d0917a7c7f5a9c1a3d3d9598f18f529dd2b8373ed988efea307a"
dependencies = [
- "js-sys",
"num-traits",
+ "plotters-backend",
+ "plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
+name = "plotters-backend"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b07fffcddc1cb3a1de753caa4e4df03b79922ba43cf882acc1bdd7e8df9f4590"
+
+[[package]]
+name = "plotters-svg"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b38a02e23bd9604b842a812063aec4ef702b57989c37b655254bb61c471ad211"
+dependencies = [
+ "plotters-backend",
+]
+
+[[package]]
name = "proc-macro2"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -462,9 +481,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "serde"
-version = "1.0.119"
+version = "1.0.123"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9bdd36f49e35b61d49efd8aa7fc068fd295961fd2286d0b2ee9a4c7a14e99cc3"
+checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae"
[[package]]
name = "serde_cbor"
@@ -478,9 +497,9 @@ dependencies = [
[[package]]
name = "serde_derive"
-version = "1.0.119"
+version = "1.0.123"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "552954ce79a059ddd5fd68c271592374bd15cab2274970380c000118aeffe1cd"
+checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31"
dependencies = [
"proc-macro2",
"quote",
@@ -518,9 +537,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "1.0.58"
+version = "1.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5"
+checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081"
dependencies = [
"proc-macro2",
"quote",
@@ -577,19 +596,19 @@ dependencies = [
[[package]]
name = "wasm-bindgen"
-version = "0.2.69"
+version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e"
+checksum = "55c0f7123de74f0dab9b7d00fd614e7b19349cd1e2f5252bbe9b1754b59433be"
dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.69"
+version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62"
+checksum = "7bc45447f0d4573f3d65720f636bbcc3dd6ce920ed704670118650bcd47764c7"
dependencies = [
"bumpalo",
"lazy_static",
@@ -602,9 +621,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.69"
+version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084"
+checksum = "3b8853882eef39593ad4174dd26fc9865a64e84026d223f63bb2c42affcbba2c"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -612,9 +631,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.69"
+version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549"
+checksum = "4133b5e7f2a531fa413b3a1695e925038a05a71cf67e87dafa295cb645a01385"
dependencies = [
"proc-macro2",
"quote",
@@ -625,15 +644,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.69"
+version = "0.2.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158"
+checksum = "dd4945e4943ae02d15c13962b38a5b1e81eadd4b71214eee75af64a4d6a4fd64"
[[package]]
name = "web-sys"
-version = "0.3.46"
+version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3"
+checksum = "c40dc691fc48003eba817c38da7113c15698142da971298003cac3ef175680b3"
dependencies = [
"js-sys",
"wasm-bindgen",
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ad6af5d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,32 @@
+# Sylt-lang
+
+Sylt is a statically checked and dynamically typed reference counted programming
+language made for game jams.
+
+## Why does this exist? Why use this instead of language X?
+
+Pfft! Why not?
+
+## Getting started
+
+Sylt is written entirely in Rust. There are two main ways of using it.
+
+1. Depend on this repository in your Cargo.toml.
+2. Clone this repository and cargo build. You can then pass .sy-files to the
+ resulting binary. Currently this way won't give you any kind of game.
+
+## Basic Usage
+
+Currently, Sylt can only run single files. The last filename given is
+run.
+
+The `-p` flag also lets you see a lot of debug output. If you want
+to debug the compiler and runtime this might be helpful.
+
+## Endgame
+
+A language that has some form of static typechecking, is easy and fast to work
+in. Performance should be good enough that you don't really have to worry about
+it.
+
+Dreams also exist of automatically updating the game when files are changed.
diff --git a/src/compiler.rs b/src/compiler.rs
index 9a1d1e5..4988c4d 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -152,15 +152,21 @@ macro_rules! push_scope {
for var in $compiler.frame().stack[ss..$compiler.stack().len()].iter().rev() {
if var.captured {
- $block.add(Op::PopUpvalue, $compiler.line());
+ add_op($compiler, $block, Op::PopUpvalue);
} else {
- $block.add(Op::Pop, $compiler.line());
+ add_op($compiler, $block, Op::Pop);
}
}
$compiler.stack_mut().truncate(ss);
};
}
+fn add_op(compiler: &Compiler, block: &mut Block, op: Op) -> usize {
+ block.add(op, compiler.line())
+}
+
+
+
impl Compiler {
pub fn new(current_file: &Path, tokens: TokenStream) -> Self {
Self {
@@ -330,7 +336,7 @@ impl Compiler {
Token::String(s) => { Value::String(Rc::from(s)) }
_ => { error!(self, "Cannot parse value."); Value::Bool(false) }
};
- block.add(Op::Constant(value), self.line());
+ add_op(self, block, Op::Constant(value));
}
fn grouping_or_tuple(&mut self, block: &mut Block) {
@@ -371,7 +377,7 @@ impl Compiler {
}
expect!(self, Token::RightParen, "Expected ')' after tuple.");
- block.add(Op::Tuple(num_args), self.line());
+ add_op(self, block, Op::Tuple(num_args));
Ok(())
}
@@ -387,7 +393,7 @@ impl Compiler {
expect!(self, Token::LeftBracket, "Expected '[' around index.");
self.expression(block);
- block.add(Op::Index, self.line());
+ add_op(self, block, Op::Index);
expect!(self, Token::RightBracket, "Expected ']' around index.");
}
@@ -399,7 +405,7 @@ impl Compiler {
_ => { error!(self, "Invalid unary operator"); Op::Neg },
};
self.parse_precedence(block, Prec::Factor);
- block.add(op, self.line());
+ add_op(self, block, op);
}
fn binary(&mut self, block: &mut Block) {
@@ -507,7 +513,7 @@ impl Compiler {
}
}
- block.add(Op::Call(arity), self.line());
+ add_op(self, block, Op::Call(arity));
}
fn function(&mut self, block: &mut Block) {
@@ -578,16 +584,16 @@ impl Compiler {
Op::Pop | Op::PopUpvalue => {}
Op::Return => { break; } ,
_ => {
- function_block.add(Op::Constant(Value::Nil), self.line());
- function_block.add(Op::Return, self.line());
+ add_op(self, &mut function_block, Op::Constant(Value::Nil));
+ add_op(self, &mut function_block, Op::Return);
break;
}
}
}
if function_block.ops.is_empty() {
- function_block.add(Op::Constant(Value::Nil), self.line());
- function_block.add(Op::Return, self.line());
+ add_op(self, &mut function_block, Op::Constant(Value::Nil));
+ add_op(self, &mut function_block, Op::Return);
}
function_block.ty = Type::Function(args, Box::new(return_type));
@@ -596,7 +602,7 @@ impl Compiler {
let func = Op::Constant(Value::Function(Vec::new(), Rc::clone(&function_block)));
self.blocks[block_id] = function_block;
- block.add(func, self.line());
+ add_op(self, block, func);
}
fn variable_expression(&mut self, block: &mut Block) {
@@ -606,16 +612,16 @@ impl Compiler {
};
if let Some(var) = self.find_variable(&name) {
if var.upvalue {
- block.add(Op::ReadUpvalue(var.slot), self.line());
+ add_op(self, block, Op::ReadUpvalue(var.slot));
} else {
- block.add(Op::ReadLocal(var.slot), self.line());
+ add_op(self, block, Op::ReadLocal(var.slot));
}
loop {
match self.peek() {
Token::Dot => {
self.eat();
if let Token::Identifier(field) = self.eat() {
- block.add(Op::Get(String::from(field)), self.line());
+ add_op(self, block, Op::Get(String::from(field)));
} else {
error!(self, "Expected fieldname after '.'.");
break;
@@ -628,12 +634,12 @@ impl Compiler {
}
}
} else if let Some(blob) = self.find_blob(&name) {
- block.add(Op::Constant(Value::Blob(blob)), self.line());
+ add_op(self, block, Op::Constant(Value::Blob(blob)));
if self.peek() == Token::LeftParen {
self.call(block);
}
} else if let Some(slot) = self.find_extern_function(&name) {
- block.add(Op::Constant(Value::ExternFunction(slot)), self.line());
+ add_op(self, block, Op::Constant(Value::ExternFunction(slot)));
self.call(block);
} else {
error!(self, format!("Using undefined variable {}.", name));
@@ -667,7 +673,7 @@ impl Compiler {
fn definition_statement(&mut self, name: &str, typ: Type, block: &mut Block) {
let slot = self.define_variable(name, typ.clone(), block);
self.expression(block);
- block.add(Op::Define(typ), self.line());
+ add_op(self, block, Op::Define(typ));
if let Ok(slot) = slot {
self.stack_mut()[slot].active = true;
@@ -699,17 +705,17 @@ impl Compiler {
if let Some(var) = self.find_variable(&name) {
if let Some(op) = op {
- block.add(Op::Copy, self.line());
+ add_op(self, block, Op::Copy);
self.expression(block);
- block.add(op, self.line());
+ add_op(self, block, op);
} else {
self.expression(block);
}
if var.upvalue {
- block.add(Op::AssignUpvalue(var.slot), self.line());
+ add_op(self, block, Op::AssignUpvalue(var.slot));
} else {
- block.add(Op::AssignLocal(var.slot), self.line());
+ add_op(self, block, Op::AssignLocal(var.slot));
}
} else {
error!(self, format!("Using undefined variable {}.", name));
@@ -738,13 +744,13 @@ impl Compiler {
fn if_statment(&mut self, block: &mut Block) {
expect!(self, Token::If, "Expected 'if' at start of if-statement.");
self.expression(block);
- let jump = block.add(Op::Illegal, self.line());
+ let jump = add_op(self, block, Op::Illegal);
self.scope(block);
if Token::Else == self.peek() {
self.eat();
- let else_jmp = block.add(Op::Illegal, self.line());
+ let else_jmp = add_op(self, block, Op::Illegal);
block.patch(Op::JmpFalse(block.curr()), jump);
match self.peek() {
@@ -781,20 +787,20 @@ impl Compiler {
let cond = block.curr();
self.expression(block);
- let cond_out = block.add(Op::Illegal, self.line());
- let cond_cont = block.add(Op::Illegal, self.line());
+ let cond_out = add_op(self, block, Op::Illegal);
+ let cond_cont = add_op(self, block, Op::Illegal);
expect!(self, Token::Comma, "Expect ',' between initalizer and loop expression.");
let inc = block.curr();
push_scope!(self, block, {
self.statement(block);
});
- block.add(Op::Jmp(cond), self.line());
+ add_op(self, block, Op::Jmp(cond));
// patch_jmp!(Op::Jmp, cond_cont => block.curr());
block.patch(Op::Jmp(block.curr()), cond_cont);
self.scope(block);
- block.add(Op::Jmp(inc), self.line());
+ add_op(self, block, Op::Jmp(inc));
block.patch(Op::JmpFalse(block.curr()), cond_out);
@@ -915,9 +921,9 @@ impl Compiler {
};
if let Some(var) = self.find_variable(&name) {
if var.upvalue {
- block.add(Op::ReadUpvalue(var.slot), self.line());
+ add_op(self, block, Op::ReadUpvalue(var.slot));
} else {
- block.add(Op::ReadLocal(var.slot), self.line());
+ add_op(self, block, Op::ReadLocal(var.slot));
}
loop {
match self.peek() {
@@ -934,7 +940,7 @@ impl Compiler {
Token::Equal => {
self.eat();
self.expression(block);
- block.add(Op::Set(field), self.line());
+ add_op(self, block, Op::Set(field));
return Ok(());
}
@@ -944,16 +950,16 @@ impl Compiler {
Token::SlashEqual => Op::Div,
_ => {
- block.add(Op::Get(field), self.line());
+ add_op(self, block, Op::Get(field));
continue;
}
};
- block.add(Op::Copy, self.line());
- block.add(Op::Get(field.clone()), self.line());
+ add_op(self, block, Op::Copy);
+ add_op(self, block, Op::Get(field.clone()));
self.eat();
self.expression(block);
- block.add(op, self.line());
- block.add(Op::Set(field), self.line());
+ add_op(self, block, op);
+ add_op(self, block, Op::Set(field));
return Ok(());
}
Token::LeftParen => {
@@ -979,7 +985,7 @@ impl Compiler {
(Token::Print, ..) => {
self.eat();
self.expression(block);
- block.add(Op::Print, self.line());
+ add_op(self, block, Op::Print);
}
(Token::Identifier(_), Token::Equal, ..) |
@@ -1016,7 +1022,7 @@ impl Compiler {
(Token::Yield, ..) => {
self.eat();
- block.add(Op::Yield, self.line());
+ add_op(self, block, Op::Yield);
}
(Token::Identifier(name), Token::ColonEqual, ..) => {
@@ -1040,12 +1046,12 @@ impl Compiler {
(Token::Ret, ..) => {
self.eat();
self.expression(block);
- block.add(Op::Return, self.line());
+ add_op(self, block, Op::Return);
}
(Token::Unreachable, ..) => {
self.eat();
- block.add(Op::Unreachable, self.line());
+ add_op(self, block, Op::Unreachable);
}
(Token::LeftBrace, ..) => {
@@ -1056,7 +1062,7 @@ impl Compiler {
_ => {
self.expression(block);
- block.add(Op::Pop, self.line());
+ add_op(self, block, Op::Pop);
}
}
@@ -1086,8 +1092,8 @@ impl Compiler {
self.statement(&mut block);
expect!(self, Token::Newline | Token::EOF, "Expect newline or EOF after expression.");
}
- block.add(Op::Constant(Value::Nil), self.line());
- block.add(Op::Return, self.line());
+ add_op(self, &mut block, Op::Constant(Value::Nil));
+ add_op(self, &mut block, Op::Return);
block.ty = Type::Function(Vec::new(), Box::new(Type::Void));
self.blocks.insert(0, Rc::new(RefCell::new(block)));
diff --git a/src/lib.rs b/src/lib.rs
index 62a2a81..46bcb54 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -57,303 +57,102 @@ pub fn run(tokens: TokenStream, path: &Path, print: bool, functions: Vec<(String
}
}
-#[cfg(test)]
-mod tests {
- use std::path::Path;
-
- use crate::error::ErrorKind;
+pub type RustFunction = fn(&[Value], bool) -> Result<Value, ErrorKind>;
- use super::{run_file, run_string};
+#[derive(Debug, Clone)]
+pub enum Type {
+ Void,
+ UnknownType,
+ Int,
+ Float,
+ Bool,
+ String,
+ Tuple(Vec<Type>),
+ Function(Vec<Type>, Box<Type>),
+ Blob(usize),
+ BlobInstance(usize),
+}
- #[macro_export]
- macro_rules! assert_errs {
- ($result:expr, [ $( $kind:pat ),* ]) => {
- eprintln!("{} => {:?}", stringify!($result), $result);
- assert!(matches!(
- $result.unwrap_err().as_slice(),
- &[$($crate::error::Error {
- kind: $kind,
- file: _,
- line: _,
- message: _,
- },
- )*]
- ))
- };
+impl PartialEq for Type {
+ fn eq(&self, other: &Self) -> bool {
+ match (self, other) {
+ (Type::Void, Type::Void) => true,
+ (Type::BlobInstance(a), Type::BlobInstance(b)) => a == b,
+ (Type::Blob(a), Type::Blob(b)) => a == b,
+ (Type::Int, Type::Int) => true,
+ (Type::Float, Type::Float) => true,
+ (Type::Bool, Type::Bool) => true,
+ (Type::String, Type::String) => true,
+ (Type::Tuple(a), Type::Tuple(b)) => {
+ a.iter().zip(b.iter()).all(|(a, b)| a == b)
+ }
+ (Type::Function(a_args, a_ret), Type::Function(b_args, b_ret)) =>
+ a_args == b_args && a_ret == b_ret,
+ _ => false,
+ }
}
+}
- #[macro_export]
- macro_rules! test_string {
- ($fn:ident, $prog:literal) => {
- #[test]
- fn $fn() {
- $crate::run_string($prog, true, Vec::new()).unwrap();
- }
- };
- ($fn:ident, $prog:literal, $errs:tt) => {
- #[test]
- fn $fn() {
- $crate::assert_errs!($crate::run_string($prog, true, Vec::new()), $errs);
+impl From<&Value> for Type {
+ fn from(value: &Value) -> Type {
+ match value {
+ Value::BlobInstance(i, _) => Type::BlobInstance(*i),
+ Value::Blob(i) => Type::Blob(*i),
+ Value::Tuple(v) => {
+ Type::Tuple(v.iter().map(|x| Type::from(x)).collect())
}
+ Value::Int(_) => Type::Int,
+ Value::Float(_) => Type::Float,
+ Value::Bool(_) => Type::Bool,
+ Value::String(_) => Type::String,
+ Value::Function(_, block) => block.borrow().ty.clone(),
+ _ => Type::Void,
}
}
+}
- #[macro_export]
- macro_rules! test_file {
- ($fn:ident, $path:literal) => {
- #[test]
- fn $fn() {
- let file = Path::new($path);
- run_file(&file, true, Vec::new()).unwrap();
- }
- };
+impl From<Value> for Type {
+ fn from(value: Value) -> Type {
+ Type::from(&value)
}
+}
- #[test]
- fn unreachable_token() {
- assert_errs!(run_string("<!>\n", true, Vec::new()), [ErrorKind::Unreachable]);
+impl Type {
+ pub fn is_unkown(&self) -> bool {
+ match self {
+ Type::UnknownType => true,
+ _ => false,
+ }
}
+}
- macro_rules! test_multiple {
- ($mod:ident, $( $fn:ident : $prog:literal ),+ $( , )? ) => {
- mod $mod {
- $( test_string!($fn, $prog); )+
+impl From<&Type> for Value {
+ fn from(ty: &Type) -> Self {
+ match ty {
+ Type::Void => Value::Nil,
+ Type::Blob(i) => Value::Blob(*i),
+ Type::BlobInstance(i) => Value::BlobInstance(*i, Rc::new(RefCell::new(Vec::new()))),
+ Type::Tuple(fields) => {
+ Value::Tuple(Rc::new(fields.iter().map(Value::from).collect()))
}
+ Type::UnknownType => Value::Unkown,
+ Type::Int => Value::Int(1),
+ Type::Float => Value::Float(1.0),
+ Type::Bool => Value::Bool(true),
+ Type::String => Value::String(Rc::new("".to_string())),
+ Type::Function(_, _) => Value::Function(
+ Vec::new(),
+ Rc::new(RefCell::new(Block::empty_with_type(ty)))),
}
}
+}
- test_multiple!(
- order_of_operations,
- terms_and_factors: "1 + 1 * 2 <=> 3
- 1 * 2 + 3 <=> 5",
- in_rhs: "5 <=> 1 * 2 + 3",
- parenthesis: "(1 + 2) * 3 <=> 9",
- negation: "-1 <=> 0 - 1
- -1 + 2 <=> 1
- -(1 + 2) <=> -3
- 1 + -1 <=> 0
- 2 * -1 <=> -2",
- );
-
- test_multiple!(
- variables,
- single_variable: "a := 1
- a <=> 1",
- two_variables: "a := 1
- b := 2
- a <=> 1
- b <=> 2",
- stack_ordering: "a := 1
- b := 2
- b <=> 2
- a <=> 1",
- assignment: "a := 1
- b := 2
- a = b
- a <=> 2
- b <=> 2",
- );
-
- test_multiple!(
- if_,
- compare_constants_equality: "if 1 == 2 {
- <!>
- }",
- compare_constants_unequality: "if 1 != 1 {
- <!>
- }",
- compare_variable: "a := 1
- if a == 0 {
- <!>
- }
- if a != 1 {
- <!>
- }",
- else_: "a := 1
- res := 0
- if a == 0 {
- <!>
- } else {
- res = 1
- }
- res <=> 1",
- else_if: "a := 1
- res := 0
- if a == 0 {
- <!>
- } else if a == 1 {
- res = 1
- } else {
- <!>
- }
- res <=> 1",
- );
-
- test_multiple!(
- fun,
- simplest: "f := fn {}
- f()",
- param_1: "f := fn a: int {}
- f(1)",
- return_1: "f := fn -> int {
- ret 1
- }
- f() <=> 1",
- param_and_return: "f := fn a: int -> int {
- ret a * 2
- }
- f(1) <=> 2
- f(5) <=> 10",
- param_2: "add := fn a: int, b: int -> int {
- ret a + b
- }
- add(1, 1) <=> 2
- add(10, 20) <=> 30",
- calls_inside_calls: "one := fn -> int {
- ret 1
- }
- add := fn a: int, b: int -> int {
- ret a + b
- }
- add(one(), one()) <=> 2
- add(add(one(), one()), one()) <=> 3
- add(one(), add(one(), one())) <=> 3",
- passing_functions: "g := fn -> int {
- ret 1
- }
- f := fn inner: fn -> int -> int {
- ret inner()
- }
- f(g) <=> 1",
- passing_functions_mixed: "g := fn a: int -> int {
- ret a * 2
- }
- f := fn inner: fn int -> int, a: int -> int {
- ret inner(a)
- }
- f(g, 2) <=> 4",
- multiple_returns: "f := fn a: int -> int {
- if a == 1 {
- ret 2
- } else {
- ret 3
- }
- }
- f(0) <=> 3
- f(1) <=> 2
- f(2) <=> 3",
- precedence: "f := fn a: int, b: int -> int {
- ret a + b
- }
- 1 + f(2, 3) <=> 6
- 2 * f(2, 3) <=> 10
- f(2, 3) - (2 + 3) <=> 0",
- factorial: "factorial : fn int -> int = fn n: int -> int {
- if n <= 1 {
- ret 1
- }
- ret n * factorial(n - 1)
- }
- factorial(5) <=> 120
- factorial(6) <=> 720
- factorial(12) <=> 479001600",
-
- returning_closures: "
-f : fn -> fn -> int = fn -> fn -> int {
- x : int = 0
- f := fn -> int {
- x = x + 1
- ret x
+impl From<Type> for Value {
+ fn from(ty: Type) -> Self {
+ Value::from(&ty)
}
- f() <=> 1
- ret f
}
-a := f()
-b := f()
-
-a() <=> 2
-a() <=> 3
-
-b() <=> 2
-b() <=> 3
-
-a() <=> 4
-"
-
- //TODO this tests doesn't terminate in proper time if we print blocks and ops
- /*
- fibonacci: "fibonacci : fn int -> int = fn n: int -> int {
- if n == 0 {
- ret 0
- } else if n == 1 {
- ret 1
- } else if n < 0 {
- <!>
- }
- ret fibonacci(n - 1) + fibonacci(n - 2)
- }
- fibonacci(10) <=> 55
- fibonacci(20) <=> 6765"
- */
- );
-
- test_multiple!(
- blob,
- simple: "blob A {}",
- instantiate: "blob A {}
- a := A()",
- field: "blob A { a: int }",
- field_assign: "blob A { a: int }
- a := A()
- a.a = 2",
- field_get: "blob A { a: int }
- a := A()
- a.a = 2
- a.a <=> 2
- 2 <=> a.a",
- multiple_fields: "blob A {
- a: int
- b: int
- }
- a := A()
- a.a = 2
- a.b = 3
- a.a + a.b <=> 5
- 5 <=> a.a + a.b"
- );
-
- test_multiple!(tuples,
- add: "(1, 2, 3, 4) + (4, 3, 2, 1) <=> (5, 5, 5, 5)",
- sub: "(1, -2, 3, -4) - (4, 3, -2, -1) <=> (-3, 1, 1, -5)",
- mul: "(0, 1, 2) * (2, 3, 4) <=> (0, 3, 8)",
- types: "a: (int, float, int) = (1, 1., 1)",
- more_types: "a: (str, bool, int) = (\"abc\", true, 1)",
- );
-
- test_file!(scoping, "progs/tests/scoping.sy");
- test_file!(for_, "progs/tests/for.sy");
-
- test_multiple!(
- op_assign,
- add: "a := 1\na += 1\na <=> 2",
- sub: "a := 2\na -= 1\na <=> 1",
- mul: "a := 2\na *= 2\na <=> 4",
- div: "a := 2\na /= 2\na <=> 1",
- cluster: "
-blob A { a: int }
-a := A()
-a.a = 0
-a.a += 1
-a.a <=> 1
-a.a *= 2
-a.a <=> 2
-a.a /= 2
-a.a <=> 1
-a.a -= 1
-a.a <=> 0"
- );
-}
#[derive(Clone)]
pub enum Value {
@@ -370,6 +169,42 @@ pub enum Value {
Nil,
}
+impl Debug for Value {
+ fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Value::Blob(i) => write!(fmt, "(blob {})", i),
+ Value::BlobInstance(i, v) => write!(fmt, "(inst {} {:?})", i, v),
+ Value::Float(f) => write!(fmt, "(float {})", f),
+ Value::Int(i) => write!(fmt, "(int {})", i),
+ Value::Bool(b) => write!(fmt, "(bool {})", b),
+ Value::String(s) => write!(fmt, "(string \"{}\")", s),
+ Value::Function(_, block) => write!(fmt, "(fn {}: {:?})", block.borrow().name, block.borrow().ty),
+ Value::ExternFunction(slot) => write!(fmt, "(extern fn {})", slot),
+ Value::Unkown => write!(fmt, "(unkown)"),
+ Value::Nil => write!(fmt, "(nil)"),
+ Value::Tuple(v) => write!(fmt, "({:?})", v),
+ }
+ }
+}
+
+impl Value {
+ fn identity(self) -> Self {
+ match self {
+ Value::Float(_) => Value::Float(1.0),
+ Value::Int(_) => Value::Int(1),
+ Value::Bool(_) => Value::Bool(true),
+ a => a,
+ }
+ }
+
+ fn is_nil(&self) -> bool {
+ match self {
+ Value::Nil => true,
+ _ => false,
+ }
+ }
+}
+
#[derive(Clone, Debug)]
pub struct UpValue {
slot: usize,
@@ -411,24 +246,83 @@ impl UpValue {
}
}
-impl Debug for Value {
- fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- Value::Blob(i) => write!(fmt, "(blob {})", i),
- Value::BlobInstance(i, v) => write!(fmt, "(inst {} {:?})", i, v),
- Value::Float(f) => write!(fmt, "(float {})", f),
- Value::Int(i) => write!(fmt, "(int {})", i),
- Value::Bool(b) => write!(fmt, "(bool {})", b),
- Value::String(s) => write!(fmt, "(string \"{}\")", s),
- Value::Function(_, block) => write!(fmt, "(fn {}: {:?})", block.borrow().name, block.borrow().ty),
- Value::ExternFunction(slot) => write!(fmt, "(extern fn {})", slot),
- Value::Unkown => write!(fmt, "(unkown)"),
- Value::Nil => write!(fmt, "(nil)"),
- Value::Tuple(v) => write!(fmt, "({:?})", v),
+#[derive(Debug, Clone)]
+pub struct Blob {
+ pub name: String,
+
+ pub name_to_field: HashMap<String, (usize, Type)>,
+}
+
+impl Blob {
+ pub fn new(name: &str) -> Self {
+ Self {
+ name: String::from(name),
+ name_to_field: HashMap::new(),
+ }
+ }
+
+ pub fn add_field(&mut self, name: &str, ty: Type) -> Result<(), ()> {
+ let size = self.name_to_field.len();
+ let entry = self.name_to_field.entry(String::from(name));
+ if matches!(entry, Entry::Occupied(_)) {
+ Err(())
+ } else {
+ entry.or_insert((size, ty));
+ Ok(())
}
}
}
+#[derive(Debug, Clone)]
+pub enum Op {
+ Illegal,
+
+ Pop,
+ PopUpvalue,
+ Copy,
+ Constant(Value),
+ Tuple(usize),
+
+ Index,
+ Get(String),
+ Set(String),
+
+ Add,
+ Sub,
+ Mul,
+ Div,
+ Neg,
+
+ And,
+ Or,
+ Not,
+
+ Jmp(usize),
+ JmpFalse(usize),
+
+ Equal, // ==
+ Less, // <
+ Greater, // >
+
+ Assert,
+ Unreachable,
+
+ ReadLocal(usize),
+ AssignLocal(usize),
+
+ ReadUpvalue(usize),
+ AssignUpvalue(usize),
+
+ Define(Type),
+
+ Call(usize),
+
+ Print,
+
+ Return,
+ Yield,
+}
+
mod op {
use super::Value;
use std::rc::Rc;
@@ -528,80 +422,6 @@ mod op {
_ => Value::Nil,
}
}
-
-}
-
-
-impl Value {
- fn identity(self) -> Self {
- match self {
- Value::Float(_) => Value::Float(1.0),
- Value::Int(_) => Value::Int(1),
- Value::Bool(_) => Value::Bool(true),
- a => a,
- }
- }
-
- fn is_nil(&self) -> bool {
- match self {
- Value::Nil => true,
- _ => false,
- }
- }
-
- fn as_type(&self) -> Type {
- Type::from(self)
- }
-}
-
-#[derive(Debug, Clone)]
-pub enum Op {
- Illegal,
-
- Pop,
- PopUpvalue,
- Copy,
- Constant(Value),
- Tuple(usize),
-
- Index,
- Get(String),
- Set(String),
-
- Add,
- Sub,
- Mul,
- Div,
- Neg,
-
- And,
- Or,
- Not,
-
- Jmp(usize),
- JmpFalse(usize),
-
- Equal, // ==
- Less, // <
- Greater, // >
-
- Assert,
- Unreachable,
-
- ReadLocal(usize),
- AssignLocal(usize),
-
- ReadUpvalue(usize),
- AssignUpvalue(usize),
-
- Define(Type),
-
- Call(usize),
-
- Print,
-
- Return,
- Yield,
}
#[derive(Debug)]
@@ -631,7 +451,7 @@ impl Block {
}
}
- pub fn from_type(ty: &Type) -> Self {
+ pub fn empty_with_type(ty: &Type) -> Self {
let mut block = Block::new("/empty/", Path::new(""), 0);
block.ty = ty.clone();
block
@@ -657,10 +477,6 @@ impl Block {
(self.file.clone(), self.line)
}
- pub fn last_op(&self) -> Option<&Op> {
- self.ops.last()
- }
-
pub fn add_line(&mut self, token_position: usize) {
if token_position != self.last_line_offset {
self.line_offsets.insert(self.curr(), token_position);
@@ -717,7 +533,6 @@ impl Block {
}
}
-
#[derive(Clone)]
pub struct Prog {
pub blocks: Vec<Rc<RefCell<Block>>>,
@@ -725,111 +540,300 @@ pub struct Prog {
pub functions: Vec<RustFunction>,
}
-#[derive(Debug, Clone)]
-pub enum Type {
- Void,
- UnknownType,
- Int,
- Float,
- Bool,
- String,
- Tuple(Vec<Type>),
- Function(Vec<Type>, Box<Type>),
- Blob(usize),
- BlobInstance(usize),
-}
+#[cfg(test)]
+mod tests {
+ use std::path::Path;
-impl PartialEq for Type {
- fn eq(&self, other: &Self) -> bool {
- match (self, other) {
- (Type::Void, Type::Void) => true,
- (Type::BlobInstance(a), Type::BlobInstance(b)) => a == b,
- (Type::Blob(a), Type::Blob(b)) => a == b,
- (Type::Int, Type::Int) => true,
- (Type::Float, Type::Float) => true,
- (Type::Bool, Type::Bool) => true,
- (Type::String, Type::String) => true,
- (Type::Tuple(a), Type::Tuple(b)) => {
- a.iter().zip(b.iter()).all(|(a, b)| a == b)
+ use crate::error::ErrorKind;
+
+ use super::{run_file, run_string};
+
+ #[macro_export]
+ macro_rules! assert_errs {
+ ($result:expr, [ $( $kind:pat ),* ]) => {
+ eprintln!("{} => {:?}", stringify!($result), $result);
+ assert!(matches!(
+ $result.unwrap_err().as_slice(),
+ &[$($crate::error::Error {
+ kind: $kind,
+ file: _,
+ line: _,
+ message: _,
+ },
+ )*]
+ ))
+ };
+ }
+
+ #[macro_export]
+ macro_rules! test_string {
+ ($fn:ident, $prog:literal) => {
+ #[test]
+ fn $fn() {
+ $crate::run_string($prog, true, Vec::new()).unwrap();
+ }
+ };
+ ($fn:ident, $prog:literal, $errs:tt) => {
+ #[test]
+ fn $fn() {
+ $crate::assert_errs!($crate::run_string($prog, true, Vec::new()), $errs);
}
- (Type::Function(a_args, a_ret), Type::Function(b_args, b_ret)) =>
- a_args == b_args && a_ret == b_ret,
- _ => false,
}
}
-}
-impl From<&Value> for Type {
- fn from(value: &Value) -> Type {
- match value {
- Value::BlobInstance(i, _) => Type::BlobInstance(*i),
- Value::Blob(i) => Type::Blob(*i),
- Value::Tuple(v) => {
- Type::Tuple(v.iter().map(|x| Type::from(x)).collect())
+ #[macro_export]
+ macro_rules! test_file {
+ ($fn:ident, $path:literal) => {
+ #[test]
+ fn $fn() {
+ let file = Path::new($path);
+ run_file(&file, true, Vec::new()).unwrap();
}
- Value::Int(_) => Type::Int,
- Value::Float(_) => Type::Float,
- Value::Bool(_) => Type::Bool,
- Value::String(_) => Type::String,
- Value::Function(_, block) => block.borrow().ty.clone(),
- _ => Type::Void,
- }
+ };
}
-}
-impl Type {
- pub fn is_unkown(&self) -> bool {
- match self {
- Type::UnknownType => true,
- _ => false,
- }
+ #[test]
+ fn unreachable_token() {
+ assert_errs!(run_string("<!>\n", true, Vec::new()), [ErrorKind::Unreachable]);
}
- pub fn as_value(&self) -> Value {
- match self {
- Type::Void => Value::Nil,
- Type::Blob(i) => Value::Blob(*i),
- Type::BlobInstance(i) => Value::BlobInstance(*i, Rc::new(RefCell::new(Vec::new()))),
- Type::Tuple(fields) => {
- Value::Tuple(Rc::new(fields.iter().map(|x| x.as_value()).collect()))
+ macro_rules! test_multiple {
+ ($mod:ident, $( $fn:ident : $prog:literal ),+ $( , )? ) => {
+ mod $mod {
+ $( test_string!($fn, $prog); )+
}
- Type::UnknownType => Value::Unkown,
- Type::Int => Value::Int(1),
- Type::Float => Value::Float(1.0),
- Type::Bool => Value::Bool(true),
- Type::String => Value::String(Rc::new("".to_string())),
- Type::Function(_, _) => Value::Function(
- Vec::new(),
- Rc::new(RefCell::new(Block::from_type(self)))),
}
}
-}
-pub type RustFunction = fn(&[Value], bool) -> Result<Value, ErrorKind>;
+ test_multiple!(
+ order_of_operations,
+ terms_and_factors: "1 + 1 * 2 <=> 3
+ 1 * 2 + 3 <=> 5",
+ in_rhs: "5 <=> 1 * 2 + 3",
+ parenthesis: "(1 + 2) * 3 <=> 9",
+ negation: "-1 <=> 0 - 1
+ -1 + 2 <=> 1
+ -(1 + 2) <=> -3
+ 1 + -1 <=> 0
+ 2 * -1 <=> -2",
+ );
-#[derive(Debug, Clone)]
-pub struct Blob {
- pub name: String,
+ test_multiple!(
+ variables,
+ single_variable: "a := 1
+ a <=> 1",
+ two_variables: "a := 1
+ b := 2
+ a <=> 1
+ b <=> 2",
+ stack_ordering: "a := 1
+ b := 2
+ b <=> 2
+ a <=> 1",
+ assignment: "a := 1
+ b := 2
+ a = b
+ a <=> 2
+ b <=> 2",
+ );
- pub name_to_field: HashMap<String, (usize, Type)>,
-}
+ test_multiple!(
+ if_,
+ compare_constants_equality: "if 1 == 2 {
+ <!>
+ }",
+ compare_constants_unequality: "if 1 != 1 {
+ <!>
+ }",
+ compare_variable: "a := 1
+ if a == 0 {
+ <!>
+ }
+ if a != 1 {
+ <!>
+ }",
+ else_: "a := 1
+ res := 0
+ if a == 0 {
+ <!>
+ } else {
+ res = 1
+ }
+ res <=> 1",
+ else_if: "a := 1
+ res := 0
+ if a == 0 {
+ <!>
+ } else if a == 1 {
+ res = 1
+ } else {
+ <!>
+ }
+ res <=> 1",
+ );
-impl Blob {
- pub fn new(name: &str) -> Self {
- Self {
- name: String::from(name),
- name_to_field: HashMap::new(),
- }
- }
+ test_multiple!(
+ fun,
+ simplest: "f := fn {}
+ f()",
+ param_1: "f := fn a: int {}
+ f(1)",
+ return_1: "f := fn -> int {
+ ret 1
+ }
+ f() <=> 1",
+ param_and_return: "f := fn a: int -> int {
+ ret a * 2
+ }
+ f(1) <=> 2
+ f(5) <=> 10",
+ param_2: "add := fn a: int, b: int -> int {
+ ret a + b
+ }
+ add(1, 1) <=> 2
+ add(10, 20) <=> 30",
+ calls_inside_calls: "one := fn -> int {
+ ret 1
+ }
+ add := fn a: int, b: int -> int {
+ ret a + b
+ }
+ add(one(), one()) <=> 2
+ add(add(one(), one()), one()) <=> 3
+ add(one(), add(one(), one())) <=> 3",
+ passing_functions: "g := fn -> int {
+ ret 1
+ }
+ f := fn inner: fn -> int -> int {
+ ret inner()
+ }
+ f(g) <=> 1",
+ passing_functions_mixed: "g := fn a: int -> int {
+ ret a * 2
+ }
+ f := fn inner: fn int -> int, a: int -> int {
+ ret inner(a)
+ }
+ f(g, 2) <=> 4",
+ multiple_returns: "f := fn a: int -> int {
+ if a == 1 {
+ ret 2
+ } else {
+ ret 3
+ }
+ }
+ f(0) <=> 3
+ f(1) <=> 2
+ f(2) <=> 3",
+ precedence: "f := fn a: int, b: int -> int {
+ ret a + b
+ }
+ 1 + f(2, 3) <=> 6
+ 2 * f(2, 3) <=> 10
+ f(2, 3) - (2 + 3) <=> 0",
+ factorial: "factorial : fn int -> int = fn n: int -> int {
+ if n <= 1 {
+ ret 1
+ }
+ ret n * factorial(n - 1)
+ }
+ factorial(5) <=> 120
+ factorial(6) <=> 720
+ factorial(12) <=> 479001600",
- pub fn add_field(&mut self, name: &str, ty: Type) -> Result<(), ()> {
- let size = self.name_to_field.len();
- let entry = self.name_to_field.entry(String::from(name));
- if matches!(entry, Entry::Occupied(_)) {
- Err(())
- } else {
- entry.or_insert((size, ty));
- Ok(())
- }
+ returning_closures: "
+f : fn -> fn -> int = fn -> fn -> int {
+ x : int = 0
+ f := fn -> int {
+ x = x + 1
+ ret x
}
+ f() <=> 1
+ ret f
+}
+
+a := f()
+b := f()
+
+a() <=> 2
+a() <=> 3
+
+b() <=> 2
+b() <=> 3
+
+a() <=> 4
+"
+
+ //TODO this tests doesn't terminate in proper time if we print blocks and ops
+ /*
+ fibonacci: "fibonacci : fn int -> int = fn n: int -> int {
+ if n == 0 {
+ ret 0
+ } else if n == 1 {
+ ret 1
+ } else if n < 0 {
+ <!>
+ }
+ ret fibonacci(n - 1) + fibonacci(n - 2)
+ }
+ fibonacci(10) <=> 55
+ fibonacci(20) <=> 6765"
+ */
+ );
+
+ test_multiple!(
+ blob,
+ simple: "blob A {}",
+ instantiate: "blob A {}
+ a := A()",
+ field: "blob A { a: int }",
+ field_assign: "blob A { a: int }
+ a := A()
+ a.a = 2",
+ field_get: "blob A { a: int }
+ a := A()
+ a.a = 2
+ a.a <=> 2
+ 2 <=> a.a",
+ multiple_fields: "blob A {
+ a: int
+ b: int
+ }
+ a := A()
+ a.a = 2
+ a.b = 3
+ a.a + a.b <=> 5
+ 5 <=> a.a + a.b"
+ );
+
+ test_multiple!(tuples,
+ add: "(1, 2, 3, 4) + (4, 3, 2, 1) <=> (5, 5, 5, 5)",
+ sub: "(1, -2, 3, -4) - (4, 3, -2, -1) <=> (-3, 1, 1, -5)",
+ mul: "(0, 1, 2) * (2, 3, 4) <=> (0, 3, 8)",
+ types: "a: (int, float, int) = (1, 1., 1)",
+ more_types: "a: (str, bool, int) = (\"abc\", true, 1)",
+ );
+
+ test_file!(scoping, "progs/tests/scoping.sy");
+ test_file!(for_, "progs/tests/for.sy");
+
+ test_multiple!(
+ op_assign,
+ add: "a := 1\na += 1\na <=> 2",
+ sub: "a := 2\na -= 1\na <=> 1",
+ mul: "a := 2\na *= 2\na <=> 4",
+ div: "a := 2\na /= 2\na <=> 1",
+ cluster: "
+blob A { a: int }
+a := A()
+a.a = 0
+a.a += 1
+a.a <=> 1
+a.a *= 2
+a.a <=> 2
+a.a /= 2
+a.a <=> 1
+a.a -= 1
+a.a <=> 0"
+ );
}
diff --git a/src/vm.rs b/src/vm.rs
index 221c3e0..474c51d 100644
--- a/src/vm.rs
+++ b/src/vm.rs
@@ -487,7 +487,7 @@ impl VM {
if *is_up {
types.push(ty.clone());
} else {
- types.push(self.stack[*slot].as_type());
+ types.push(Type::from(&self.stack[*slot]));
}
}
@@ -517,7 +517,7 @@ impl VM {
Op::Get(field) => {
let inst = self.pop();
if let Value::BlobInstance(ty, _) = inst {
- let value = self.blobs[ty].name_to_field.get(&field).unwrap().1.as_value();
+ let value = Value::from(&self.blobs[ty].name_to_field.get(&field).unwrap().1);
self.push(value);
} else {
self.push(Value::Nil);
@@ -544,13 +544,13 @@ impl VM {
}
Op::ReadUpvalue(slot) => {
- let value = self.frame().block.borrow().ups[slot].2.as_value();
+ let value = Value::from(&self.frame().block.borrow().ups[slot].2);
self.push(value);
}
Op::AssignUpvalue(slot) => {
let var = self.frame().block.borrow().ups[slot].2.clone();
- let up = self.pop().as_type();
+ let up = self.pop().into();
if var != up {
error!(self, ErrorKind::TypeError(op, vec![var, up]),
"Incorrect type for upvalue.".to_string());
@@ -561,8 +561,8 @@ impl VM {
let a = self.pop();
let inner = self.frame().block.borrow();
let ret = inner.ret();
- if a.as_type() != *ret {
- error!(self, ErrorKind::TypeError(op, vec![a.as_type(),
+ if Type::from(&a) != *ret {
+ error!(self, ErrorKind::TypeError(op, vec![a.into(),
ret.clone()]),
"Not matching return type.".to_string());
}
@@ -573,7 +573,7 @@ impl VM {
}
Op::Define(ref ty) => {
- let top_type = self.stack.last().unwrap().as_type();
+ let top_type = self.stack.last().unwrap().into();
match (ty, top_type) {
(Type::UnknownType, top_type)
if top_type != Type::UnknownType => {}
@@ -601,7 +601,7 @@ impl VM {
}
for (slot, ty) in blob.name_to_field.values() {
- values[*slot] = ty.as_value();
+ values[*slot] = ty.into();
}
self.pop();
@@ -618,7 +618,7 @@ impl VM {
}
let stack_args = &self.stack[self.stack.len() - args.len()..];
- let stack_args: Vec<_> = stack_args.iter().map(|x| x.as_type()).collect();
+ let stack_args: Vec<_> = stack_args.iter().map(|x| x.into()).collect();
if args != &stack_args {
error!(self,
ErrorKind::TypeError(op.clone(), vec![]),
@@ -626,7 +626,7 @@ impl VM {
args, stack_args));
}
- self.stack[new_base] = block.borrow().ret().as_value();
+ self.stack[new_base] = block.borrow().ret().into();
self.stack.truncate(new_base + 1);
}
@@ -645,7 +645,7 @@ impl VM {
}
_ => {
error!(self,
- ErrorKind::TypeError(op.clone(), vec![self.stack[new_base].as_type()]),
+ ErrorKind::TypeError(op.clone(), vec![Type::from(&self.stack[new_base])]),
format!("Tried to call non-function {:?}", self.stack[new_base]));
}
}
@@ -654,7 +654,7 @@ impl VM {
Op::JmpFalse(_) => {
match self.pop() {
Value::Bool(_) => {},
- a => { error!(self, ErrorKind::TypeError(op.clone(), vec![a.as_type()])) },
+ a => { error!(self, ErrorKind::TypeError(op.clone(), vec![a.into()])) },
}
}
_ => {
@@ -672,7 +672,7 @@ impl VM {
self.push(Value::Function(Vec::new(), Rc::clone(&block)));
for arg in block.borrow().args() {
- self.push(arg.as_value());
+ self.push(arg.into());
}
self.frames.push(Frame {
diff --git a/sylt_macro/src/lib.rs b/sylt_macro/src/lib.rs
index 241ef7d..fc43b5c 100644
--- a/sylt_macro/src/lib.rs
+++ b/sylt_macro/src/lib.rs
@@ -51,7 +51,7 @@ pub fn extern_function(tokens: TokenStream) -> TokenStream {
let pat = block.pattern.clone();
let ty = block.return_ty.clone();
quote! {
- #pat => { Ok(#ty.as_value()) }
+ #pat => { Ok(sylt::Value::from(#ty)) }
}
}).collect();