Compare commits

...

11 commits

14 changed files with 1070 additions and 79 deletions

103
Cargo.lock generated
View file

@ -3,5 +3,104 @@
version = 3
[[package]]
name = "prout"
version = "0.1.0"
name = "ahash"
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"

View file

@ -1,8 +1,13 @@
[package]
name = "prout"
version = "0.1.0"
name = "porte"
version = "0.4.0"
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
[dependencies]
chumsky = "0.8"

21
LICENSE Normal file
View 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.

View file

@ -1,15 +1,15 @@
# PROUT
# PORTE
* Programmable
* Repeatable
* Opinionated
* Understandable
* Repeatable
* Tiny
* Extensible
## 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
@ -17,11 +17,11 @@ PROUT is a minimal programming language providing tools to automate file managem
```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
View 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
View 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
View 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);
}

View file

@ -19,6 +19,15 @@ impl ParserBuilder {
pub fn prelude(&mut self, name: String, value: 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 {
@ -26,45 +35,45 @@ pub struct 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
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 {
scopes: HashMap::new(),
};
let mut builder = ParserBuilder::new();
operation(&mut builder);
builder.append_globals(&mut body);
let mut parser = Self::new();
let parser_scope = ParserScope::new_root();
let mut parser_builder = ParserBuilder::new();
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 main_scope_id = parser.parse_syntax_tree_scope(body, &parser_scope);
let Self { scopes } = parser;
execution_tree::Program {
main_scope_id: body_scope_id,
main_scope_id,
scopes,
}
}
pub fn parse_ast_scope(
pub fn parse_syntax_tree_scope(
&mut self,
ast_scope: syntax_tree::Scope,
syntax_tree_scope: syntax_tree::Scope,
parser_scope: &ParserScope,
) -> 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 parent_scope_id = parser_scope.get_parent_id();
let expressions = instructions
.into_iter()
.map(|expression| self.parse_expression(expression, &parser_scope))
.map(|expression| self.parse_expression(expression, parser_scope))
.collect();
let local_variables = parser_scope.local_variable_ids();
@ -86,8 +95,8 @@ impl Parser {
let inner = expression.into_inner();
match inner {
syntax_tree::ExprInner::Scope(scope) => {
let parser_scope = parser_scope.make_child_common();
let scope = self.parse_ast_scope(scope, &parser_scope);
let parser_scope = parser_scope.child_common();
let scope = self.parse_syntax_tree_scope(scope, &parser_scope);
execution_tree::Expr::new_scope(scope)
}
syntax_tree::ExprInner::Literal(literal) => {
@ -187,12 +196,12 @@ impl Parser {
parameter_names,
} = function_definition;
let parser_scope = parser_scope.make_child_common();
let parser_scope = parser_scope.child_function();
let parameter_ids = parameter_names
.into_iter()
.map(|name| parser_scope.add_name(name))
.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 {
body_scope_id,
@ -209,7 +218,7 @@ impl Parser {
let variable_id = parser_scope
.get_variable_id(&name)
.expect("call of undeclared function");
.unwrap_or_else(|| panic!("call of undeclared function '{name}'"));
let parameters = arguments
.into_iter()
.map(|argument| self.parse_expression(argument, parser_scope))
@ -246,8 +255,8 @@ impl Parser {
) -> execution_tree::Loop {
let syntax_tree::Loop { body } = loop_;
let parser_scope = parser_scope.make_child_loop();
let body_scope_id = self.parse_ast_scope(body, &parser_scope);
let parser_scope = parser_scope.child_loop();
let body_scope_id = self.parse_syntax_tree_scope(body, &parser_scope);
execution_tree::Loop { body_scope_id }
}
@ -328,7 +337,7 @@ impl ParserScopeVariables {
#[derive(Debug, Clone)]
pub struct ParserScope {
variables: Rc<Mutex<ParserScopeVariables>>,
next_global_id: Rc<Mutex<Id>>,
next_id: Rc<Mutex<Id>>,
current_id: Id,
parent_id: Option<Id>,
current_function_scope_id: Option<Id>,
@ -338,7 +347,7 @@ pub struct ParserScope {
impl ParserScope {
pub fn new_root() -> Self {
let current_id = Id::zero();
let next_global_id = current_id.next();
let next_id = current_id.next();
let variables = ParserScopeVariables {
local_variables: HashMap::new(),
parent_scope: None,
@ -346,21 +355,21 @@ impl ParserScope {
Self {
parent_id: None,
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)),
current_function_scope_id: None,
current_loop_scope_id: None,
}
}
pub fn make_child_common(&self) -> Self {
pub fn child_common(&self) -> Self {
let variables = ParserScopeVariables {
local_variables: HashMap::new(),
parent_scope: Some(self.variables.clone()),
};
Self {
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)),
current_id: self.request_new_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 {
local_variables: HashMap::new(),
parent_scope: Some(self.variables.clone()),
@ -378,15 +387,15 @@ impl ParserScope {
Self {
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)),
current_id,
current_function_scope_id: Some(current_id.clone()),
current_function_scope_id: Some(current_id),
current_loop_scope_id: None,
}
}
pub fn make_child_loop(&self) -> Self {
pub fn child_loop(&self) -> Self {
let variables = ParserScopeVariables {
local_variables: HashMap::new(),
parent_scope: Some(self.variables.clone()),
@ -396,28 +405,28 @@ impl ParserScope {
Self {
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)),
current_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 {
self.current_id.clone()
self.current_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> {
self.current_loop_scope_id.clone()
self.current_loop_scope_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> {
@ -425,7 +434,7 @@ impl ParserScope {
}
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;
*next_id_ref = id.next();
id

View file

@ -1,3 +1,6 @@
use std::{env::args, fs};
pub mod demo;
pub mod execution_tree;
pub mod prelude;
pub mod runtime;
@ -5,7 +8,15 @@ pub mod syntax_tree;
pub mod value;
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]
@ -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")]),
]),
};
let exec = Parser::parse(ast, |builder| std_prelude(builder));
let exec = Parser::parse(ast, std_prelude);
println!("\n\n\n-- running: --");
let _result = Runtime::new().execute(&exec);
}

View file

@ -1,14 +1,148 @@
use std::collections::HashMap;
use crate::{
execution_tree::parser::ParserBuilder,
value::{function::Function, Value},
};
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();
println!("{to_print:?}");
Value::Bool(true)
let str = value_to_string(to_print);
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())
}

View file

@ -24,12 +24,12 @@ impl FrameBuilder {
}
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 {
scope_id: Id,
_scope_id: Id,
variables: HashMap<Id, Value>,
}
@ -52,9 +52,9 @@ impl Frame {
builder(&mut frame_builder);
let FrameBuilder { variables } = frame_builder;
let scope_id = scope_id.clone();
let scope_id = *scope_id;
Self {
scope_id,
_scope_id: scope_id,
variables,
}
}
@ -99,6 +99,12 @@ impl Stack {
}
}
impl Default for Stack {
fn default() -> Self {
Self::new()
}
}
pub struct ShortCircuit {
value: Value,
destination_scope_id: Id,
@ -138,9 +144,9 @@ impl ExecReturn {
}
}
impl Into<ExecReturn> for Value {
fn into(self) -> ExecReturn {
ExecReturn::new_value(self)
impl From<Value> for ExecReturn {
fn from(val: Value) -> Self {
ExecReturn::new_value(val)
}
}
@ -169,7 +175,7 @@ impl Runtime {
where
F: FnOnce(&mut FrameBuilder),
{
let scope = program.scopes.get(&scope_id).unwrap();
let scope = program.scopes.get(scope_id).unwrap();
let Scope {
parent_scope_id: _,
expressions,
@ -210,9 +216,7 @@ impl Runtime {
ExprInner::FnDef(function_definition) => {
self.execute_function_definition(function_definition)
}
ExprInner::FnCall(function_call) => {
self.execute_function_call(function_call, program).into()
}
ExprInner::FnCall(function_call) => self.execute_function_call(function_call, program),
ExprInner::FnRet(function_return) => {
self.execute_function_return(function_return, program)
}
@ -272,7 +276,7 @@ impl Runtime {
parameter_ids: argument_ids,
body_scope_id,
} = 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);
value.into()
}
@ -367,7 +371,7 @@ impl Runtime {
}) => 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 {
@ -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 {
@ -432,3 +436,9 @@ impl Runtime {
}
}
}
impl Default for Runtime {
fn default() -> Self {
Self::new()
}
}

View file

@ -9,6 +9,10 @@ pub struct Program {
pub struct Expr(pub Box<ExprInner>);
impl Expr {
pub fn new(inner: ExprInner) -> Self {
Self(Box::new(inner))
}
pub fn inner(&self) -> &ExprInner {
let Self(inner) = self;
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
where
T: Into<Value>,
@ -164,3 +234,5 @@ pub struct Cond {
pub arm_true: Expr,
pub arm_false: Option<Expr>,
}
pub mod parser;

422
src/syntax_tree/parser.rs Normal file
View 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
}
}
*/

View file

@ -2,7 +2,7 @@ use std::collections::HashMap;
use self::function::Function;
pub mod function {
use crate::execution_tree::{Expr, Id};
use crate::execution_tree::Id;
use super::Value;
@ -55,9 +55,9 @@ pub mod function {
}
}
impl Into<Value> for Function {
fn into(self) -> Value {
Value::Function(self)
impl From<Function> for Value {
fn from(val: Function) -> Self {
Value::Function(val)
}
}
}
@ -72,6 +72,34 @@ pub enum 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> {
match self {
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
where
T: Into<Value>,