Compare commits
11 commits
70f1f90cc7
...
28377bd3c2
Author | SHA1 | Date | |
---|---|---|---|
28377bd3c2 | |||
c08c263f32 | |||
a4ff72d8ea | |||
cefff00db3 | |||
fe99fddc29 | |||
dcf2bac9b6 | |||
a32f62847e | |||
dac8ac1e9c | |||
7aa98bdd04 | |||
43f42ecccd | |||
|
bb384243cd |
14 changed files with 1070 additions and 79 deletions
103
Cargo.lock
generated
103
Cargo.lock
generated
|
@ -3,5 +3,104 @@
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prout"
|
name = "ahash"
|
||||||
version = "0.1.0"
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
|
||||||
|
dependencies = [
|
||||||
|
"const-random",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chumsky"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d02796e4586c6c41aeb68eae9bfb4558a522c35f1430c14b40136c3706e09e4"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-random"
|
||||||
|
version = "0.1.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "368a7a772ead6ce7e1de82bfb04c485f3db8ec744f72925af5735e29a22cc18e"
|
||||||
|
dependencies = [
|
||||||
|
"const-random-macro",
|
||||||
|
"proc-macro-hack",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-random-macro"
|
||||||
|
version = "0.1.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"once_cell",
|
||||||
|
"proc-macro-hack",
|
||||||
|
"tiny-keccak",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crunchy"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.2.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.126"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "porte"
|
||||||
|
version = "0.4.0"
|
||||||
|
dependencies = [
|
||||||
|
"chumsky",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-hack"
|
||||||
|
version = "0.5.20+deprecated"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tiny-keccak"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
|
||||||
|
dependencies = [
|
||||||
|
"crunchy",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.10.2+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
[package]
|
[package]
|
||||||
name = "prout"
|
name = "porte"
|
||||||
version = "0.1.0"
|
version = "0.4.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
authors = ["JOLIMAITRE Matthieu <matthieu@imagevo.fr>"]
|
||||||
|
description = "A minimal programming language providing tools to automate file management like backups, building process or unit testing."
|
||||||
|
license = "MIT"
|
||||||
|
repository = "https://github.com/MajorBarnulf/porte"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
chumsky = "0.8"
|
||||||
|
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 MajorBarnulf
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
14
README.md
14
README.md
|
@ -1,15 +1,15 @@
|
||||||
|
|
||||||
# PROUT
|
# PORTE
|
||||||
|
|
||||||
* Programmable
|
* Programmable
|
||||||
* Repeatable
|
|
||||||
* Opinionated
|
* Opinionated
|
||||||
* Understandable
|
* Repeatable
|
||||||
* Tiny
|
* Tiny
|
||||||
|
* Extensible
|
||||||
|
|
||||||
## Description
|
## Description
|
||||||
|
|
||||||
PROUT is a minimal programming language providing tools to automate file management like backups, building process or unit testing.
|
PORTE is a minimal programming language providing tools to automate file management like backups, building process or unit testing.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
@ -17,11 +17,11 @@ PROUT is a minimal programming language providing tools to automate file managem
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|
||||||
$ prout hello_world.pr
|
$ porte hello-world.pr
|
||||||
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Author
|
## Authors
|
||||||
|
|
||||||
- JOLIMAITRE Matthieu <matthieu@imagevo.fr>, developper
|
- JOLIMAITRE Matthieu <matthieu@imagevo.fr>
|
||||||
|
|
117
examples/array.pr
Normal file
117
examples/array.pr
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
|
||||||
|
for: (from, to, f) => {
|
||||||
|
index: from;
|
||||||
|
loop {
|
||||||
|
if not(inf(index, to)) break true;
|
||||||
|
f(index);
|
||||||
|
index <- add(index, 1)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
array_new: () => {
|
||||||
|
r: obj();
|
||||||
|
r <- set(r, "len", 0);
|
||||||
|
r
|
||||||
|
};
|
||||||
|
|
||||||
|
array_len: (self) => {
|
||||||
|
get(self, "len")
|
||||||
|
};
|
||||||
|
|
||||||
|
array_get: (self, index) => {
|
||||||
|
get(self, str(index))
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach: (arr, f) => {
|
||||||
|
l: array_len(arr);
|
||||||
|
for(0, l, (i) => {
|
||||||
|
f(array_get(arr, i))
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
array_push: (self, e) => {
|
||||||
|
i: array_len(self);
|
||||||
|
self <- set(self, str(i), e);
|
||||||
|
|
||||||
|
len: add (i, 1);
|
||||||
|
self <- set(self, "len", len);
|
||||||
|
self
|
||||||
|
};
|
||||||
|
|
||||||
|
array_pop: (self) => {
|
||||||
|
l: array_len(self);
|
||||||
|
if eq(l, 0) return false;
|
||||||
|
|
||||||
|
i: sub(l, 1);
|
||||||
|
e: array_get(self, i);
|
||||||
|
self <- set(self, str(i), false);
|
||||||
|
|
||||||
|
rest: array_new();
|
||||||
|
foreach(self, (item) => { if not(eq(item, false)) rest <- array_push(rest, item) });
|
||||||
|
|
||||||
|
r: obj();
|
||||||
|
r <- set(r, "tail", e);
|
||||||
|
r <- set(r, "rest", rest);
|
||||||
|
r
|
||||||
|
};
|
||||||
|
|
||||||
|
array_swap: (self, i, j) => {
|
||||||
|
i_value: array_get(self, i);
|
||||||
|
j_value: array_get(self, j);
|
||||||
|
self <- set(self, str(j), i_value);
|
||||||
|
self <- set(self, str(i), j_value);
|
||||||
|
self
|
||||||
|
};
|
||||||
|
|
||||||
|
array_sort: (self, cmp) => {
|
||||||
|
l: array_len(self);
|
||||||
|
for(0, sub(l, 1), (i) => {
|
||||||
|
i_min: i;
|
||||||
|
for(add(i, 1), l, (j) => {
|
||||||
|
e_j: array_get(self, j);
|
||||||
|
e_min: array_get(self, i_min);
|
||||||
|
if inf(cmp(e_j, e_min), 0) i_min <- j
|
||||||
|
});
|
||||||
|
self <- array_swap(self, i, i_min)
|
||||||
|
});
|
||||||
|
self
|
||||||
|
};
|
||||||
|
|
||||||
|
array_print: (self) => {
|
||||||
|
l: array_len(self);
|
||||||
|
r: "[ ";
|
||||||
|
for(0, l, (i) => {
|
||||||
|
r <- add(r, array_get(self, i));
|
||||||
|
if not(eq(i, sub(l, 1))) r <- add(r, ", ")
|
||||||
|
});
|
||||||
|
r <- add(r, " ]");
|
||||||
|
out(r)
|
||||||
|
};
|
||||||
|
|
||||||
|
"########################";
|
||||||
|
"# main #";
|
||||||
|
"########################";
|
||||||
|
|
||||||
|
a: array_new();
|
||||||
|
out("new:");
|
||||||
|
array_print(a);
|
||||||
|
|
||||||
|
a <- array_push(a, 1);
|
||||||
|
a <- array_push(a, 4);
|
||||||
|
a <- array_push(a, 6);
|
||||||
|
a <- array_push(a, 3);
|
||||||
|
a <- array_push(a, 2);
|
||||||
|
out("");
|
||||||
|
out("pushed:");
|
||||||
|
array_print(a);
|
||||||
|
|
||||||
|
r: array_pop(a);
|
||||||
|
a <- get(r, "rest");
|
||||||
|
out("");
|
||||||
|
out("popped:");
|
||||||
|
array_print(a);
|
||||||
|
|
||||||
|
a <- array_sort(a, (a, b) => { if sup(a, b) 1 else -1 });
|
||||||
|
out("");
|
||||||
|
out("sorted:");
|
||||||
|
array_print(a);
|
36
examples/hello-world.pr
Normal file
36
examples/hello-world.pr
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
|
||||||
|
hello: "hello";
|
||||||
|
porte: "porte";
|
||||||
|
|
||||||
|
line: hello;
|
||||||
|
line <- add(line, " ");
|
||||||
|
line <- add(line, porte);
|
||||||
|
|
||||||
|
say-it: () => {
|
||||||
|
out(line)
|
||||||
|
};
|
||||||
|
|
||||||
|
say-it();
|
||||||
|
|
||||||
|
|
||||||
|
for: (from, to, incr, fn) => {
|
||||||
|
index: from;
|
||||||
|
loop {
|
||||||
|
if not(inf(index, to)) break true;
|
||||||
|
result: fn(index);
|
||||||
|
index <- add(index, incr)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for(0, 3, 0.5, (index) => {
|
||||||
|
out(add("index: ", index))
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
a: 1;
|
||||||
|
b: 2;
|
||||||
|
c: 3;
|
||||||
|
a <- b <- c;
|
||||||
|
out(add("a :", a));
|
||||||
|
out(add("b :", b));
|
||||||
|
out(add("c :", c));
|
21
src/demo.rs
Normal file
21
src/demo.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#[test]
|
||||||
|
fn ast() {
|
||||||
|
use crate::execution_tree;
|
||||||
|
use crate::syntax_tree;
|
||||||
|
|
||||||
|
let text = r#"
|
||||||
|
a: 3;
|
||||||
|
{
|
||||||
|
a <- 4;
|
||||||
|
a: 5
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let tree = syntax_tree::parser::ParserWrapper::new()
|
||||||
|
.parse(text)
|
||||||
|
.unwrap();
|
||||||
|
dbg!(&tree);
|
||||||
|
|
||||||
|
let executable = execution_tree::parser::Parser::parse(tree, |_| ());
|
||||||
|
dbg!(&executable);
|
||||||
|
}
|
|
@ -19,6 +19,15 @@ impl ParserBuilder {
|
||||||
pub fn prelude(&mut self, name: String, value: Value) {
|
pub fn prelude(&mut self, name: String, value: Value) {
|
||||||
self.prelude.push((name, value));
|
self.prelude.push((name, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn append_globals(&self, body: &mut syntax_tree::Scope) {
|
||||||
|
let prelude = self.prelude.clone();
|
||||||
|
for (name, value) in prelude.into_iter().rev() {
|
||||||
|
let value = syntax_tree::Expr::new_literal(value);
|
||||||
|
let expression = syntax_tree::Expr::new_variable_definition(name, value);
|
||||||
|
body.instructions.insert(0, expression);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Parser {
|
pub struct Parser {
|
||||||
|
@ -26,45 +35,45 @@ pub struct Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parser {
|
impl Parser {
|
||||||
pub fn parse<F>(ast: syntax_tree::Program, builder: F) -> execution_tree::Program
|
fn new() -> Self {
|
||||||
|
let scopes = HashMap::new();
|
||||||
|
Self { scopes }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse<F>(syntax_tree: syntax_tree::Program, builder: F) -> execution_tree::Program
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut ParserBuilder),
|
F: FnOnce(&mut ParserBuilder),
|
||||||
{
|
{
|
||||||
let syntax_tree::Program { mut body } = ast;
|
let operation = builder;
|
||||||
|
let syntax_tree::Program { mut body } = syntax_tree;
|
||||||
|
|
||||||
let mut parser = Self {
|
let mut builder = ParserBuilder::new();
|
||||||
scopes: HashMap::new(),
|
operation(&mut builder);
|
||||||
};
|
builder.append_globals(&mut body);
|
||||||
|
|
||||||
|
let mut parser = Self::new();
|
||||||
let parser_scope = ParserScope::new_root();
|
let parser_scope = ParserScope::new_root();
|
||||||
let mut parser_builder = ParserBuilder::new();
|
let main_scope_id = parser.parse_syntax_tree_scope(body, &parser_scope);
|
||||||
builder(&mut parser_builder);
|
|
||||||
for (name, value) in parser_builder.prelude.into_iter().rev() {
|
|
||||||
let value = syntax_tree::Expr::new_literal(value);
|
|
||||||
let expression = syntax_tree::Expr::new_variable_definition(name, value);
|
|
||||||
body.instructions.insert(0, expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
let body_scope_id = parser.parse_ast_scope(body, &parser_scope);
|
|
||||||
|
|
||||||
let Self { scopes } = parser;
|
let Self { scopes } = parser;
|
||||||
|
|
||||||
execution_tree::Program {
|
execution_tree::Program {
|
||||||
main_scope_id: body_scope_id,
|
main_scope_id,
|
||||||
scopes,
|
scopes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_ast_scope(
|
pub fn parse_syntax_tree_scope(
|
||||||
&mut self,
|
&mut self,
|
||||||
ast_scope: syntax_tree::Scope,
|
syntax_tree_scope: syntax_tree::Scope,
|
||||||
parser_scope: &ParserScope,
|
parser_scope: &ParserScope,
|
||||||
) -> execution_tree::Id {
|
) -> execution_tree::Id {
|
||||||
let syntax_tree::Scope { instructions } = ast_scope;
|
let syntax_tree::Scope { instructions } = syntax_tree_scope;
|
||||||
|
|
||||||
let scope_id = parser_scope.get_current_id();
|
let scope_id = parser_scope.get_current_id();
|
||||||
let parent_scope_id = parser_scope.get_parent_id();
|
let parent_scope_id = parser_scope.get_parent_id();
|
||||||
let expressions = instructions
|
let expressions = instructions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|expression| self.parse_expression(expression, &parser_scope))
|
.map(|expression| self.parse_expression(expression, parser_scope))
|
||||||
.collect();
|
.collect();
|
||||||
let local_variables = parser_scope.local_variable_ids();
|
let local_variables = parser_scope.local_variable_ids();
|
||||||
|
|
||||||
|
@ -86,8 +95,8 @@ impl Parser {
|
||||||
let inner = expression.into_inner();
|
let inner = expression.into_inner();
|
||||||
match inner {
|
match inner {
|
||||||
syntax_tree::ExprInner::Scope(scope) => {
|
syntax_tree::ExprInner::Scope(scope) => {
|
||||||
let parser_scope = parser_scope.make_child_common();
|
let parser_scope = parser_scope.child_common();
|
||||||
let scope = self.parse_ast_scope(scope, &parser_scope);
|
let scope = self.parse_syntax_tree_scope(scope, &parser_scope);
|
||||||
execution_tree::Expr::new_scope(scope)
|
execution_tree::Expr::new_scope(scope)
|
||||||
}
|
}
|
||||||
syntax_tree::ExprInner::Literal(literal) => {
|
syntax_tree::ExprInner::Literal(literal) => {
|
||||||
|
@ -187,12 +196,12 @@ impl Parser {
|
||||||
parameter_names,
|
parameter_names,
|
||||||
} = function_definition;
|
} = function_definition;
|
||||||
|
|
||||||
let parser_scope = parser_scope.make_child_common();
|
let parser_scope = parser_scope.child_function();
|
||||||
let parameter_ids = parameter_names
|
let parameter_ids = parameter_names
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|name| parser_scope.add_name(name))
|
.map(|name| parser_scope.add_name(name))
|
||||||
.collect();
|
.collect();
|
||||||
let body_scope_id = self.parse_ast_scope(body, &parser_scope);
|
let body_scope_id = self.parse_syntax_tree_scope(body, &parser_scope);
|
||||||
|
|
||||||
execution_tree::FnDef {
|
execution_tree::FnDef {
|
||||||
body_scope_id,
|
body_scope_id,
|
||||||
|
@ -209,7 +218,7 @@ impl Parser {
|
||||||
|
|
||||||
let variable_id = parser_scope
|
let variable_id = parser_scope
|
||||||
.get_variable_id(&name)
|
.get_variable_id(&name)
|
||||||
.expect("call of undeclared function");
|
.unwrap_or_else(|| panic!("call of undeclared function '{name}'"));
|
||||||
let parameters = arguments
|
let parameters = arguments
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|argument| self.parse_expression(argument, parser_scope))
|
.map(|argument| self.parse_expression(argument, parser_scope))
|
||||||
|
@ -246,8 +255,8 @@ impl Parser {
|
||||||
) -> execution_tree::Loop {
|
) -> execution_tree::Loop {
|
||||||
let syntax_tree::Loop { body } = loop_;
|
let syntax_tree::Loop { body } = loop_;
|
||||||
|
|
||||||
let parser_scope = parser_scope.make_child_loop();
|
let parser_scope = parser_scope.child_loop();
|
||||||
let body_scope_id = self.parse_ast_scope(body, &parser_scope);
|
let body_scope_id = self.parse_syntax_tree_scope(body, &parser_scope);
|
||||||
|
|
||||||
execution_tree::Loop { body_scope_id }
|
execution_tree::Loop { body_scope_id }
|
||||||
}
|
}
|
||||||
|
@ -328,7 +337,7 @@ impl ParserScopeVariables {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ParserScope {
|
pub struct ParserScope {
|
||||||
variables: Rc<Mutex<ParserScopeVariables>>,
|
variables: Rc<Mutex<ParserScopeVariables>>,
|
||||||
next_global_id: Rc<Mutex<Id>>,
|
next_id: Rc<Mutex<Id>>,
|
||||||
current_id: Id,
|
current_id: Id,
|
||||||
parent_id: Option<Id>,
|
parent_id: Option<Id>,
|
||||||
current_function_scope_id: Option<Id>,
|
current_function_scope_id: Option<Id>,
|
||||||
|
@ -338,7 +347,7 @@ pub struct ParserScope {
|
||||||
impl ParserScope {
|
impl ParserScope {
|
||||||
pub fn new_root() -> Self {
|
pub fn new_root() -> Self {
|
||||||
let current_id = Id::zero();
|
let current_id = Id::zero();
|
||||||
let next_global_id = current_id.next();
|
let next_id = current_id.next();
|
||||||
let variables = ParserScopeVariables {
|
let variables = ParserScopeVariables {
|
||||||
local_variables: HashMap::new(),
|
local_variables: HashMap::new(),
|
||||||
parent_scope: None,
|
parent_scope: None,
|
||||||
|
@ -346,21 +355,21 @@ impl ParserScope {
|
||||||
Self {
|
Self {
|
||||||
parent_id: None,
|
parent_id: None,
|
||||||
current_id,
|
current_id,
|
||||||
next_global_id: Rc::new(Mutex::new(next_global_id)),
|
next_id: Rc::new(Mutex::new(next_id)),
|
||||||
variables: Rc::new(Mutex::new(variables)),
|
variables: Rc::new(Mutex::new(variables)),
|
||||||
current_function_scope_id: None,
|
current_function_scope_id: None,
|
||||||
current_loop_scope_id: None,
|
current_loop_scope_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_child_common(&self) -> Self {
|
pub fn child_common(&self) -> Self {
|
||||||
let variables = ParserScopeVariables {
|
let variables = ParserScopeVariables {
|
||||||
local_variables: HashMap::new(),
|
local_variables: HashMap::new(),
|
||||||
parent_scope: Some(self.variables.clone()),
|
parent_scope: Some(self.variables.clone()),
|
||||||
};
|
};
|
||||||
Self {
|
Self {
|
||||||
parent_id: Some(self.get_current_id()),
|
parent_id: Some(self.get_current_id()),
|
||||||
next_global_id: self.next_global_id.clone(),
|
next_id: self.next_id.clone(),
|
||||||
variables: Rc::new(Mutex::new(variables)),
|
variables: Rc::new(Mutex::new(variables)),
|
||||||
current_id: self.request_new_id(),
|
current_id: self.request_new_id(),
|
||||||
current_function_scope_id: self.get_current_function_id(),
|
current_function_scope_id: self.get_current_function_id(),
|
||||||
|
@ -368,7 +377,7 @@ impl ParserScope {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_child_function(&self) -> Self {
|
pub fn child_function(&self) -> Self {
|
||||||
let variables = ParserScopeVariables {
|
let variables = ParserScopeVariables {
|
||||||
local_variables: HashMap::new(),
|
local_variables: HashMap::new(),
|
||||||
parent_scope: Some(self.variables.clone()),
|
parent_scope: Some(self.variables.clone()),
|
||||||
|
@ -378,15 +387,15 @@ impl ParserScope {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
parent_id: Some(self.get_current_id()),
|
parent_id: Some(self.get_current_id()),
|
||||||
next_global_id: self.next_global_id.clone(),
|
next_id: self.next_id.clone(),
|
||||||
variables: Rc::new(Mutex::new(variables)),
|
variables: Rc::new(Mutex::new(variables)),
|
||||||
current_id,
|
current_id,
|
||||||
current_function_scope_id: Some(current_id.clone()),
|
current_function_scope_id: Some(current_id),
|
||||||
current_loop_scope_id: None,
|
current_loop_scope_id: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_child_loop(&self) -> Self {
|
pub fn child_loop(&self) -> Self {
|
||||||
let variables = ParserScopeVariables {
|
let variables = ParserScopeVariables {
|
||||||
local_variables: HashMap::new(),
|
local_variables: HashMap::new(),
|
||||||
parent_scope: Some(self.variables.clone()),
|
parent_scope: Some(self.variables.clone()),
|
||||||
|
@ -396,28 +405,28 @@ impl ParserScope {
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
parent_id: Some(self.get_current_id()),
|
parent_id: Some(self.get_current_id()),
|
||||||
next_global_id: self.next_global_id.clone(),
|
next_id: self.next_id.clone(),
|
||||||
variables: Rc::new(Mutex::new(variables)),
|
variables: Rc::new(Mutex::new(variables)),
|
||||||
current_id,
|
current_id,
|
||||||
current_function_scope_id: self.get_current_function_id(),
|
current_function_scope_id: self.get_current_function_id(),
|
||||||
current_loop_scope_id: Some(current_id.clone()),
|
current_loop_scope_id: Some(current_id),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_current_id(&self) -> Id {
|
pub fn get_current_id(&self) -> Id {
|
||||||
self.current_id.clone()
|
self.current_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_current_function_id(&self) -> Option<Id> {
|
pub fn get_current_function_id(&self) -> Option<Id> {
|
||||||
self.current_function_scope_id.clone()
|
self.current_function_scope_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_current_loop_id(&self) -> Option<Id> {
|
pub fn get_current_loop_id(&self) -> Option<Id> {
|
||||||
self.current_loop_scope_id.clone()
|
self.current_loop_scope_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_parent_id(&self) -> Option<Id> {
|
pub fn get_parent_id(&self) -> Option<Id> {
|
||||||
self.parent_id.clone()
|
self.parent_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_variable_id(&self, name: &str) -> Option<Id> {
|
pub fn get_variable_id(&self, name: &str) -> Option<Id> {
|
||||||
|
@ -425,7 +434,7 @@ impl ParserScope {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn request_new_id(&self) -> Id {
|
fn request_new_id(&self) -> Id {
|
||||||
let mut next_id_ref = self.next_global_id.lock().unwrap();
|
let mut next_id_ref = self.next_id.lock().unwrap();
|
||||||
let id = *next_id_ref;
|
let id = *next_id_ref;
|
||||||
*next_id_ref = id.next();
|
*next_id_ref = id.next();
|
||||||
id
|
id
|
||||||
|
|
17
src/main.rs
17
src/main.rs
|
@ -1,3 +1,6 @@
|
||||||
|
use std::{env::args, fs};
|
||||||
|
|
||||||
|
pub mod demo;
|
||||||
pub mod execution_tree;
|
pub mod execution_tree;
|
||||||
pub mod prelude;
|
pub mod prelude;
|
||||||
pub mod runtime;
|
pub mod runtime;
|
||||||
|
@ -5,7 +8,15 @@ pub mod syntax_tree;
|
||||||
pub mod value;
|
pub mod value;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("Hello, world!");
|
use prelude::std_prelude;
|
||||||
|
|
||||||
|
let path = args().nth(1).expect("[error]: usage 'porte <path>'");
|
||||||
|
let input = fs::read_to_string(path).expect("file not found");
|
||||||
|
let ast_parser = syntax_tree::parser::ParserWrapper::new();
|
||||||
|
let parsed = ast_parser.parse(&input).unwrap();
|
||||||
|
let executable = execution_tree::parser::Parser::parse(parsed, std_prelude);
|
||||||
|
let mut runtime = runtime::Runtime::new();
|
||||||
|
runtime.execute(&executable);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -29,11 +40,11 @@ fn it_works() {
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expr::new_function_call("my_print", vec!["hello, PROUT".into()]),
|
Expr::new_function_call("my_print", vec!["hello, PORTE".into()]),
|
||||||
Expr::new_function_call("print", vec![Expr::new_variable_call("a")]),
|
Expr::new_function_call("print", vec![Expr::new_variable_call("a")]),
|
||||||
]),
|
]),
|
||||||
};
|
};
|
||||||
let exec = Parser::parse(ast, |builder| std_prelude(builder));
|
let exec = Parser::parse(ast, std_prelude);
|
||||||
println!("\n\n\n-- running: --");
|
println!("\n\n\n-- running: --");
|
||||||
let _result = Runtime::new().execute(&exec);
|
let _result = Runtime::new().execute(&exec);
|
||||||
}
|
}
|
||||||
|
|
142
src/prelude.rs
142
src/prelude.rs
|
@ -1,14 +1,148 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
execution_tree::parser::ParserBuilder,
|
execution_tree::parser::ParserBuilder,
|
||||||
value::{function::Function, Value},
|
value::{function::Function, Value},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn std_prelude(builder: &mut ParserBuilder) {
|
pub fn std_prelude(builder: &mut ParserBuilder) {
|
||||||
builder.prelude("print".into(), Function::new_native(1, print).into())
|
type FunctOper = fn(Vec<Value>) -> Value;
|
||||||
|
let functions: Vec<(_, _, FunctOper)> = vec![
|
||||||
|
("out", 1, out),
|
||||||
|
("add", 2, add),
|
||||||
|
("sub", 2, sub),
|
||||||
|
("eq", 2, eq),
|
||||||
|
("sup", 2, sup),
|
||||||
|
("inf", 2, inf),
|
||||||
|
("and", 2, and),
|
||||||
|
("or", 2, or),
|
||||||
|
("not", 1, not),
|
||||||
|
("str", 2, str),
|
||||||
|
("obj", 0, obj),
|
||||||
|
("set", 3, set),
|
||||||
|
("get", 2, get),
|
||||||
|
];
|
||||||
|
|
||||||
|
for (name, arg_count, closure) in functions {
|
||||||
|
builder.prelude(name.into(), Function::new_native(arg_count, closure).into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print(args: Vec<Value>) -> Value {
|
fn out(args: Vec<Value>) -> Value {
|
||||||
let to_print = args.get(0).unwrap();
|
let to_print = args.get(0).unwrap();
|
||||||
println!("{to_print:?}");
|
let str = value_to_string(to_print);
|
||||||
Value::Bool(true)
|
println!("{str}");
|
||||||
|
to_print.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add(args: Vec<Value>) -> Value {
|
||||||
|
let lhs = args.get(0).unwrap();
|
||||||
|
let rhs = args.get(1).unwrap();
|
||||||
|
match (lhs, rhs) {
|
||||||
|
(Value::Number(l), Value::Number(r)) => (l + r).into(),
|
||||||
|
(Value::String(l), Value::String(r)) => format!("{l}{r}").into(),
|
||||||
|
(Value::Number(l), Value::String(r)) => format!("{l}{r}").into(),
|
||||||
|
(Value::String(l), Value::Number(r)) => format!("{l}{r}").into(),
|
||||||
|
_ => panic!("adding incompatible types"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sub(args: Vec<Value>) -> Value {
|
||||||
|
let lhs = args.get(0).unwrap();
|
||||||
|
let rhs = args.get(1).unwrap();
|
||||||
|
match (lhs, rhs) {
|
||||||
|
(Value::Number(l), Value::Number(r)) => (l - r).into(),
|
||||||
|
_ => panic!("substracting non-numbers"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eq(args: Vec<Value>) -> Value {
|
||||||
|
let lhs = args.get(0).unwrap();
|
||||||
|
let rhs = args.get(1).unwrap();
|
||||||
|
match (lhs, rhs) {
|
||||||
|
(Value::Bool(l), Value::Bool(r)) => (l == r).into(),
|
||||||
|
(Value::Number(l), Value::Number(r)) => (l == r).into(),
|
||||||
|
(Value::String(l), Value::String(r)) => (l == r).into(),
|
||||||
|
_ => false.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sup(args: Vec<Value>) -> Value {
|
||||||
|
let lhs = args.get(0).unwrap();
|
||||||
|
let rhs = args.get(1).unwrap();
|
||||||
|
match (lhs, rhs) {
|
||||||
|
(Value::Number(l), Value::Number(r)) => (l > r).into(),
|
||||||
|
_ => panic!("comparing non-numeric"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inf(args: Vec<Value>) -> Value {
|
||||||
|
let lhs = args.get(0).unwrap();
|
||||||
|
let rhs = args.get(1).unwrap();
|
||||||
|
match (lhs, rhs) {
|
||||||
|
(Value::Number(l), Value::Number(r)) => (l < r).into(),
|
||||||
|
_ => panic!("comparing non-numeric"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn and(args: Vec<Value>) -> Value {
|
||||||
|
let lhs = args.get(0).unwrap();
|
||||||
|
let rhs = args.get(1).unwrap();
|
||||||
|
match (lhs, rhs) {
|
||||||
|
(Value::Bool(l), Value::Bool(r)) => (*l && *r).into(),
|
||||||
|
_ => panic!("intersection of non-boolean"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn or(args: Vec<Value>) -> Value {
|
||||||
|
let lhs = args.get(0).unwrap();
|
||||||
|
let rhs = args.get(1).unwrap();
|
||||||
|
match (lhs, rhs) {
|
||||||
|
(Value::Bool(l), Value::Bool(r)) => (*l || *r).into(),
|
||||||
|
_ => panic!("union of non-boolean"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn not(args: Vec<Value>) -> Value {
|
||||||
|
let input = args.get(0).unwrap();
|
||||||
|
let result = !input.as_bool().expect("complementing non-bool");
|
||||||
|
result.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn value_to_string(input: &Value) -> String {
|
||||||
|
match input {
|
||||||
|
Value::None => "None".to_string(),
|
||||||
|
Value::Bool(b) => format!("{b}"),
|
||||||
|
Value::Number(n) => format!("{n}"),
|
||||||
|
Value::String(s) => s.clone(),
|
||||||
|
Value::Object(_) => "[object]".into(),
|
||||||
|
Value::Function(_) => "[function]".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn str(args: Vec<Value>) -> Value {
|
||||||
|
let input = args.get(0).unwrap();
|
||||||
|
value_to_string(input).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn obj(_: Vec<Value>) -> Value {
|
||||||
|
Value::Object(HashMap::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(args: Vec<Value>) -> Value {
|
||||||
|
let mut object = args.get(0).unwrap().as_object().unwrap().clone();
|
||||||
|
let name = args.get(1).unwrap().as_string().unwrap().to_string();
|
||||||
|
let value = args.get(2).unwrap().clone();
|
||||||
|
if let Value::Bool(false) = value {
|
||||||
|
object.remove(&name);
|
||||||
|
} else {
|
||||||
|
object.insert(name, value);
|
||||||
|
}
|
||||||
|
object.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(args: Vec<Value>) -> Value {
|
||||||
|
let object = args.get(0).unwrap().as_object().unwrap();
|
||||||
|
let name = args.get(1).unwrap().as_string().unwrap();
|
||||||
|
object.get(name).cloned().unwrap_or_else(|| false.into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,12 +24,12 @@ impl FrameBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn variable(&mut self, variable_id: &Id, default: Value) {
|
pub fn variable(&mut self, variable_id: &Id, default: Value) {
|
||||||
self.variables.insert(variable_id.clone(), default);
|
self.variables.insert(*variable_id, default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Frame {
|
pub struct Frame {
|
||||||
scope_id: Id,
|
_scope_id: Id,
|
||||||
variables: HashMap<Id, Value>,
|
variables: HashMap<Id, Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,9 +52,9 @@ impl Frame {
|
||||||
builder(&mut frame_builder);
|
builder(&mut frame_builder);
|
||||||
|
|
||||||
let FrameBuilder { variables } = frame_builder;
|
let FrameBuilder { variables } = frame_builder;
|
||||||
let scope_id = scope_id.clone();
|
let scope_id = *scope_id;
|
||||||
Self {
|
Self {
|
||||||
scope_id,
|
_scope_id: scope_id,
|
||||||
variables,
|
variables,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,12 @@ impl Stack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Stack {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ShortCircuit {
|
pub struct ShortCircuit {
|
||||||
value: Value,
|
value: Value,
|
||||||
destination_scope_id: Id,
|
destination_scope_id: Id,
|
||||||
|
@ -138,9 +144,9 @@ impl ExecReturn {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<ExecReturn> for Value {
|
impl From<Value> for ExecReturn {
|
||||||
fn into(self) -> ExecReturn {
|
fn from(val: Value) -> Self {
|
||||||
ExecReturn::new_value(self)
|
ExecReturn::new_value(val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +175,7 @@ impl Runtime {
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut FrameBuilder),
|
F: FnOnce(&mut FrameBuilder),
|
||||||
{
|
{
|
||||||
let scope = program.scopes.get(&scope_id).unwrap();
|
let scope = program.scopes.get(scope_id).unwrap();
|
||||||
let Scope {
|
let Scope {
|
||||||
parent_scope_id: _,
|
parent_scope_id: _,
|
||||||
expressions,
|
expressions,
|
||||||
|
@ -210,9 +216,7 @@ impl Runtime {
|
||||||
ExprInner::FnDef(function_definition) => {
|
ExprInner::FnDef(function_definition) => {
|
||||||
self.execute_function_definition(function_definition)
|
self.execute_function_definition(function_definition)
|
||||||
}
|
}
|
||||||
ExprInner::FnCall(function_call) => {
|
ExprInner::FnCall(function_call) => self.execute_function_call(function_call, program),
|
||||||
self.execute_function_call(function_call, program).into()
|
|
||||||
}
|
|
||||||
ExprInner::FnRet(function_return) => {
|
ExprInner::FnRet(function_return) => {
|
||||||
self.execute_function_return(function_return, program)
|
self.execute_function_return(function_return, program)
|
||||||
}
|
}
|
||||||
|
@ -272,7 +276,7 @@ impl Runtime {
|
||||||
parameter_ids: argument_ids,
|
parameter_ids: argument_ids,
|
||||||
body_scope_id,
|
body_scope_id,
|
||||||
} = function_definition;
|
} = function_definition;
|
||||||
let value = Function::new_constructed(argument_ids.clone(), body_scope_id.clone());
|
let value = Function::new_constructed(argument_ids.clone(), *body_scope_id);
|
||||||
let value = Value::Function(value);
|
let value = Value::Function(value);
|
||||||
value.into()
|
value.into()
|
||||||
}
|
}
|
||||||
|
@ -367,7 +371,7 @@ impl Runtime {
|
||||||
}) => value,
|
}) => value,
|
||||||
};
|
};
|
||||||
|
|
||||||
ExecReturn::new_short_circuit(value, function_scope_id.clone())
|
ExecReturn::new_short_circuit(value, *function_scope_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_loop(&mut self, loop_: &Loop, program: &Program) -> ExecReturn {
|
pub fn execute_loop(&mut self, loop_: &Loop, program: &Program) -> ExecReturn {
|
||||||
|
@ -401,7 +405,7 @@ impl Runtime {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ExecReturn::new_short_circuit(value, loop_scope_id.clone())
|
ExecReturn::new_short_circuit(value, *loop_scope_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn execute_condition(&mut self, condition: &Cond, program: &Program) -> ExecReturn {
|
pub fn execute_condition(&mut self, condition: &Cond, program: &Program) -> ExecReturn {
|
||||||
|
@ -432,3 +436,9 @@ impl Runtime {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Runtime {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -9,6 +9,10 @@ pub struct Program {
|
||||||
pub struct Expr(pub Box<ExprInner>);
|
pub struct Expr(pub Box<ExprInner>);
|
||||||
|
|
||||||
impl Expr {
|
impl Expr {
|
||||||
|
pub fn new(inner: ExprInner) -> Self {
|
||||||
|
Self(Box::new(inner))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn inner(&self) -> &ExprInner {
|
pub fn inner(&self) -> &ExprInner {
|
||||||
let Self(inner) = self;
|
let Self(inner) = self;
|
||||||
inner
|
inner
|
||||||
|
@ -76,6 +80,72 @@ impl Expr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Scope> for Expr {
|
||||||
|
fn from(input: Scope) -> Self {
|
||||||
|
Self::new(ExprInner::Scope(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Literal> for Expr {
|
||||||
|
fn from(input: Literal) -> Self {
|
||||||
|
Self::new(ExprInner::Literal(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<VarDef> for Expr {
|
||||||
|
fn from(input: VarDef) -> Self {
|
||||||
|
Self::new(ExprInner::VarDef(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<VarAssign> for Expr {
|
||||||
|
fn from(input: VarAssign) -> Self {
|
||||||
|
Self::new(ExprInner::VarAssign(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<VarCall> for Expr {
|
||||||
|
fn from(input: VarCall) -> Self {
|
||||||
|
Self::new(ExprInner::VarCall(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FnDef> for Expr {
|
||||||
|
fn from(input: FnDef) -> Self {
|
||||||
|
Self::new(ExprInner::FnDef(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FnCall> for Expr {
|
||||||
|
fn from(input: FnCall) -> Self {
|
||||||
|
Self::new(ExprInner::FnCall(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<FnRet> for Expr {
|
||||||
|
fn from(input: FnRet) -> Self {
|
||||||
|
Self::new(ExprInner::FnRet(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Loop> for Expr {
|
||||||
|
fn from(input: Loop) -> Self {
|
||||||
|
Self::new(ExprInner::Loop(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<LoopBr> for Expr {
|
||||||
|
fn from(input: LoopBr) -> Self {
|
||||||
|
Self::new(ExprInner::LoopBr(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Cond> for Expr {
|
||||||
|
fn from(input: Cond) -> Self {
|
||||||
|
Self::new(ExprInner::Cond(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> From<T> for Expr
|
impl<T> From<T> for Expr
|
||||||
where
|
where
|
||||||
T: Into<Value>,
|
T: Into<Value>,
|
||||||
|
@ -164,3 +234,5 @@ pub struct Cond {
|
||||||
pub arm_true: Expr,
|
pub arm_true: Expr,
|
||||||
pub arm_false: Option<Expr>,
|
pub arm_false: Option<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod parser;
|
||||||
|
|
422
src/syntax_tree/parser.rs
Normal file
422
src/syntax_tree/parser.rs
Normal file
|
@ -0,0 +1,422 @@
|
||||||
|
use super::*;
|
||||||
|
use chumsky::{prelude::*, text::whitespace};
|
||||||
|
|
||||||
|
pub trait AbstractParser<T>: Parser<char, T, Error = Simple<char>> {}
|
||||||
|
|
||||||
|
impl<T, U: Parser<char, T, Error = Simple<char>>> AbstractParser<T> for U {}
|
||||||
|
|
||||||
|
pub fn debugging_expression_parser() -> impl AbstractParser<Expr> + Clone {
|
||||||
|
variable_call_parser().padded().map(|v| v.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn scope_parser(expression: impl AbstractParser<Expr> + Clone) -> impl AbstractParser<Scope> {
|
||||||
|
let open_list = expression.separated_by(just(';'));
|
||||||
|
let closed_list = open_list.clone().then_ignore(just(';').then(whitespace()));
|
||||||
|
open_list
|
||||||
|
.map(|instructions| Scope { instructions })
|
||||||
|
.or(closed_list.map(|mut instructions| {
|
||||||
|
instructions.push(Value::None.into());
|
||||||
|
Scope { instructions }
|
||||||
|
}))
|
||||||
|
.padded()
|
||||||
|
.delimited_by(just('{'), just('}'))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_scope_parser() {
|
||||||
|
let parser = scope_parser(debugging_expression_parser());
|
||||||
|
let value = parser.parse("{ arbre }");
|
||||||
|
dbg!(value.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add objects ?
|
||||||
|
pub fn literal_value() -> impl AbstractParser<Value> {
|
||||||
|
let bool = just("false")
|
||||||
|
.map(|_| false.into())
|
||||||
|
.or(just("true").map(|_| true.into()));
|
||||||
|
|
||||||
|
let none = just("none").map(|_| Value::None);
|
||||||
|
|
||||||
|
let string = just('\"')
|
||||||
|
.ignore_then(none_of("\"").repeated())
|
||||||
|
.then_ignore(just('\"'))
|
||||||
|
.map(|v| String::from_iter(&v).into());
|
||||||
|
|
||||||
|
let frac = just('.').chain(text::digits(10));
|
||||||
|
let number = just('-')
|
||||||
|
.or_not()
|
||||||
|
.chain(text::int(10))
|
||||||
|
.chain::<char, _, _>(frac.or_not().flatten())
|
||||||
|
.collect::<String>()
|
||||||
|
.from_str()
|
||||||
|
.unwrapped()
|
||||||
|
.map(|n: f64| n.into());
|
||||||
|
|
||||||
|
bool.or(none).or(string).or(number)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_literal_value() {
|
||||||
|
let parser = literal_value();
|
||||||
|
let value = parser.parse("5");
|
||||||
|
assert_eq!(value.unwrap().as_number().unwrap(), 5.);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name() -> impl AbstractParser<String> + Clone {
|
||||||
|
let first = one_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
||||||
|
let rest = first.clone().or(one_of("1234567890-_+/*"));
|
||||||
|
|
||||||
|
first.then(rest.repeated()).map(|(f, v)| {
|
||||||
|
let rest = String::from_iter(&v);
|
||||||
|
format!("{f}{rest}")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn variable_definition_parser(
|
||||||
|
expression: impl AbstractParser<Expr>,
|
||||||
|
) -> impl AbstractParser<VarDef> {
|
||||||
|
name()
|
||||||
|
.then_ignore(just(":").padded())
|
||||||
|
.then(expression)
|
||||||
|
.map(|(name, value)| VarDef { name, value })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_variable_definition_parser() {
|
||||||
|
let parser = variable_definition_parser(debugging_expression_parser());
|
||||||
|
let value = parser.parse("a : b");
|
||||||
|
dbg!(value.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn variable_assignement_parser(
|
||||||
|
expression: impl AbstractParser<Expr>,
|
||||||
|
) -> impl AbstractParser<VarAssign> {
|
||||||
|
name()
|
||||||
|
.then_ignore(just("<-").padded())
|
||||||
|
.then(expression)
|
||||||
|
.map(|(name, value)| VarAssign { name, value })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_variable_assignement_parser() {
|
||||||
|
let parser = variable_assignement_parser(debugging_expression_parser());
|
||||||
|
let value = parser.parse("arbre <- expr");
|
||||||
|
dbg!(value.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn variable_call_parser() -> impl AbstractParser<VarCall> + Clone {
|
||||||
|
name().map(|name| VarCall { name })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_variable_call_parser() {
|
||||||
|
let parser = variable_call_parser();
|
||||||
|
let value = parser.parse("arbre");
|
||||||
|
assert_eq!(value.unwrap().name, "arbre".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn function_definition_parser(
|
||||||
|
expression: impl AbstractParser<Expr> + Clone,
|
||||||
|
) -> impl AbstractParser<FnDef> {
|
||||||
|
let parameters = name().separated_by(just(',').padded());
|
||||||
|
let body = scope_parser(expression);
|
||||||
|
parameters
|
||||||
|
.padded()
|
||||||
|
.delimited_by(just('('), just(')'))
|
||||||
|
.then_ignore(just("=>").padded())
|
||||||
|
.then(body)
|
||||||
|
.map(|(parameter_names, body)| FnDef {
|
||||||
|
body,
|
||||||
|
parameter_names,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_function_definition_parser() {
|
||||||
|
let parser = function_definition_parser(debugging_expression_parser());
|
||||||
|
let value = parser.parse("(a) => { b }");
|
||||||
|
dbg!(value.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn function_call_parser(expression: impl AbstractParser<Expr>) -> impl AbstractParser<FnCall> {
|
||||||
|
let parameters = expression.separated_by(just(',').padded());
|
||||||
|
name()
|
||||||
|
.then(
|
||||||
|
parameters
|
||||||
|
.padded()
|
||||||
|
.delimited_by(just('('), just(')'))
|
||||||
|
.padded(),
|
||||||
|
)
|
||||||
|
.map(|(name, arguments)| FnCall { arguments, name })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_function_call_parser() {
|
||||||
|
let parser = function_call_parser(debugging_expression_parser());
|
||||||
|
let value = parser.parse("f( a , b )");
|
||||||
|
dbg!(value.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn function_return_parser(expression: impl AbstractParser<Expr>) -> impl AbstractParser<FnRet> {
|
||||||
|
just("return")
|
||||||
|
.ignore_then(expression.or_not())
|
||||||
|
.map(|expr| expr.unwrap_or(From::<Value>::from(true.into())))
|
||||||
|
.map(|value| FnRet { value })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_function_return_parser() {
|
||||||
|
let parser = function_return_parser(debugging_expression_parser());
|
||||||
|
let value = parser.parse("return a");
|
||||||
|
dbg!(value.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn loop_parser(expression: impl AbstractParser<Expr> + Clone) -> impl AbstractParser<Loop> {
|
||||||
|
just("loop")
|
||||||
|
.then(whitespace())
|
||||||
|
.ignore_then(scope_parser(expression))
|
||||||
|
.map(|body| Loop { body })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_loop_parser() {
|
||||||
|
let parser = loop_parser(debugging_expression_parser());
|
||||||
|
let value = parser.parse("loop { a}");
|
||||||
|
dbg!(value.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn loop_break_parser(expression: impl AbstractParser<Expr>) -> impl AbstractParser<LoopBr> {
|
||||||
|
just("break")
|
||||||
|
.ignore_then(just(" ").then(whitespace()).ignore_then(expression))
|
||||||
|
.map(|value| LoopBr { value })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_loop_break_parser() {
|
||||||
|
let parser = loop_break_parser(debugging_expression_parser());
|
||||||
|
let value = parser.parse("break a");
|
||||||
|
dbg!(value.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn condition_parser(
|
||||||
|
expression: impl AbstractParser<Expr> + Clone,
|
||||||
|
) -> impl AbstractParser<Cond> {
|
||||||
|
just("if ")
|
||||||
|
.ignore_then(expression.clone())
|
||||||
|
.then(expression.clone())
|
||||||
|
.then(just("else ").ignore_then(expression).or_not())
|
||||||
|
.map(|((condition, arm_true), arm_false)| Cond {
|
||||||
|
condition,
|
||||||
|
arm_true,
|
||||||
|
arm_false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_condition_parser() {
|
||||||
|
let parser = condition_parser(debugging_expression_parser());
|
||||||
|
let value = parser.parse("if a t else f");
|
||||||
|
dbg!(value.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _sugar_parser(expression: impl AbstractParser<Expr> + Clone) -> impl AbstractParser<Expr> {
|
||||||
|
let add = expression
|
||||||
|
.clone()
|
||||||
|
.then_ignore(just(" + "))
|
||||||
|
.then(expression.clone())
|
||||||
|
.map(|(l, r)| {
|
||||||
|
FnCall {
|
||||||
|
name: "add".into(),
|
||||||
|
arguments: vec![l, r],
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
});
|
||||||
|
|
||||||
|
let sub = expression
|
||||||
|
.clone()
|
||||||
|
.then_ignore(just(" - "))
|
||||||
|
.then(expression.clone())
|
||||||
|
.map(|(l, r)| {
|
||||||
|
FnCall {
|
||||||
|
name: "sub".into(),
|
||||||
|
arguments: vec![l, r],
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
});
|
||||||
|
|
||||||
|
let eq = expression
|
||||||
|
.clone()
|
||||||
|
.then_ignore(just(" == "))
|
||||||
|
.then(expression.clone())
|
||||||
|
.map(|(l, r)| {
|
||||||
|
FnCall {
|
||||||
|
name: "eq".into(),
|
||||||
|
arguments: vec![l, r],
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
});
|
||||||
|
|
||||||
|
let sup = expression
|
||||||
|
.clone()
|
||||||
|
.then_ignore(just(" > "))
|
||||||
|
.then(expression.clone())
|
||||||
|
.map(|(l, r)| {
|
||||||
|
FnCall {
|
||||||
|
name: "sup".into(),
|
||||||
|
arguments: vec![l, r],
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
});
|
||||||
|
|
||||||
|
let inf = expression
|
||||||
|
.clone()
|
||||||
|
.then_ignore(just(" < "))
|
||||||
|
.then(expression.clone())
|
||||||
|
.map(|(l, r)| {
|
||||||
|
FnCall {
|
||||||
|
name: "inf".into(),
|
||||||
|
arguments: vec![l, r],
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
});
|
||||||
|
|
||||||
|
let or = expression
|
||||||
|
.clone()
|
||||||
|
.then_ignore(just(" || "))
|
||||||
|
.then(expression.clone())
|
||||||
|
.map(|(l, r)| {
|
||||||
|
FnCall {
|
||||||
|
name: "or".into(),
|
||||||
|
arguments: vec![l, r],
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
});
|
||||||
|
|
||||||
|
let and = expression
|
||||||
|
.clone()
|
||||||
|
.then_ignore(just(" && "))
|
||||||
|
.then(expression)
|
||||||
|
.map(|(l, r)| {
|
||||||
|
FnCall {
|
||||||
|
name: "and".into(),
|
||||||
|
arguments: vec![l, r],
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
});
|
||||||
|
|
||||||
|
eq.or(add).or(sub).or(sup).or(inf).or(or).or(and)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expression_parser() -> impl AbstractParser<Expr> {
|
||||||
|
let expression = recursive(|expression| {
|
||||||
|
//let sugar = sugar_parser(expression.clone());
|
||||||
|
let condition = condition_parser(expression.clone()).map(|i| i.into());
|
||||||
|
let function_definition = function_definition_parser(expression.clone()).map(|i| i.into());
|
||||||
|
let function_return = function_return_parser(expression.clone()).map(|i| i.into());
|
||||||
|
let loop_ = loop_parser(expression.clone()).map(|i| i.into());
|
||||||
|
let loop_break = loop_break_parser(expression.clone()).map(|i| i.into());
|
||||||
|
let scope = scope_parser(expression.clone()).map(|i| i.into());
|
||||||
|
let litteral = literal_value().map(Expr::new_literal);
|
||||||
|
let variable_definition = variable_definition_parser(expression.clone()).map(|i| i.into());
|
||||||
|
let variable_assignment = variable_assignement_parser(expression.clone()).map(|i| i.into());
|
||||||
|
let function_call = function_call_parser(expression.clone()).map(|i| i.into());
|
||||||
|
let variable_call = variable_call_parser().map(|i| i.into());
|
||||||
|
|
||||||
|
//sugar
|
||||||
|
// .or(condition)
|
||||||
|
condition
|
||||||
|
.or(function_definition)
|
||||||
|
.or(function_return)
|
||||||
|
.or(loop_)
|
||||||
|
.or(loop_break)
|
||||||
|
.or(scope)
|
||||||
|
.or(litteral)
|
||||||
|
.or(variable_definition)
|
||||||
|
.or(variable_assignment)
|
||||||
|
.or(function_call)
|
||||||
|
.or(variable_call)
|
||||||
|
.padded()
|
||||||
|
});
|
||||||
|
expression
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_expression_parser() {
|
||||||
|
let text = r##"if a b"##;
|
||||||
|
let parser = expression_parser();
|
||||||
|
let value = parser.parse(text);
|
||||||
|
dbg!(value.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parser() -> impl AbstractParser<Program> {
|
||||||
|
let scope = expression_parser()
|
||||||
|
.separated_by(just(';').padded())
|
||||||
|
.then_ignore(just(';').padded().or_not())
|
||||||
|
.then_ignore(end());
|
||||||
|
|
||||||
|
scope.map(|instructions| {
|
||||||
|
let body = Scope { instructions };
|
||||||
|
Program { body }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parser() {
|
||||||
|
let example = r##"
|
||||||
|
print(3);
|
||||||
|
|
||||||
|
a: 3;
|
||||||
|
|
||||||
|
b: (a) => {
|
||||||
|
print("a")
|
||||||
|
};
|
||||||
|
|
||||||
|
f(a)
|
||||||
|
|
||||||
|
"##;
|
||||||
|
let parser = parser();
|
||||||
|
let e = parser.parse(example);
|
||||||
|
dbg!(e.unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ParserWrapper {
|
||||||
|
inner: Box<dyn AbstractParser<Program>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParserWrapper {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let inner = Box::new(parser());
|
||||||
|
ParserWrapper { inner }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(&self, input: &str) -> Result<Program, Vec<Simple<char>>> {
|
||||||
|
self.inner.parse(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ParserWrapper {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// example
|
||||||
|
|
||||||
|
my-print: (input) => {
|
||||||
|
concatenated: {
|
||||||
|
"now I would like to interject for a moment" + input
|
||||||
|
};
|
||||||
|
print(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
a: 5.2;
|
||||||
|
loop {
|
||||||
|
if a <= 0 {
|
||||||
|
break true
|
||||||
|
} else {
|
||||||
|
a <- a - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
42
src/value.rs
42
src/value.rs
|
@ -2,7 +2,7 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use self::function::Function;
|
use self::function::Function;
|
||||||
pub mod function {
|
pub mod function {
|
||||||
use crate::execution_tree::{Expr, Id};
|
use crate::execution_tree::Id;
|
||||||
|
|
||||||
use super::Value;
|
use super::Value;
|
||||||
|
|
||||||
|
@ -55,9 +55,9 @@ pub mod function {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<Value> for Function {
|
impl From<Function> for Value {
|
||||||
fn into(self) -> Value {
|
fn from(val: Function) -> Self {
|
||||||
Value::Function(self)
|
Value::Function(val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,34 @@ pub enum Value {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
|
pub fn as_bool(&self) -> Option<bool> {
|
||||||
|
match self {
|
||||||
|
Self::Bool(b) => Some(*b),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_number(&self) -> Option<f64> {
|
||||||
|
match self {
|
||||||
|
Self::Number(n) => Some(*n),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_string(&self) -> Option<&str> {
|
||||||
|
match self {
|
||||||
|
Self::String(s) => Some(s),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_object(&self) -> Option<&HashMap<String, Value>> {
|
||||||
|
match self {
|
||||||
|
Self::Object(h) => Some(h),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_function(&self) -> Option<&Function> {
|
pub fn as_function(&self) -> Option<&Function> {
|
||||||
match self {
|
match self {
|
||||||
Self::Function(function) => Some(function),
|
Self::Function(function) => Some(function),
|
||||||
|
@ -104,6 +132,12 @@ impl From<&str> for Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<HashMap<String, Value>> for Value {
|
||||||
|
fn from(value: HashMap<String, Value>) -> Self {
|
||||||
|
Self::Object(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> From<Option<T>> for Value
|
impl<T> From<Option<T>> for Value
|
||||||
where
|
where
|
||||||
T: Into<Value>,
|
T: Into<Value>,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue