Compare commits

..

No commits in common. "28377bd3c271e70afc27c297c19a28884d70d288" and "70f1f90cc737b36d63b6627f45a68fa5a63c239c" have entirely different histories.

14 changed files with 79 additions and 1070 deletions

103
Cargo.lock generated
View file

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

View file

@ -1,13 +1,8 @@
[package]
name = "porte"
version = "0.4.0"
name = "prout"
version = "0.1.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
View file

@ -1,21 +0,0 @@
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 @@
# PORTE
# PROUT
* Programmable
* Opinionated
* Repeatable
* Opinionated
* Understandable
* Tiny
* Extensible
## Description
PORTE is a minimal programming language providing tools to automate file management like backups, building process or unit testing.
PROUT is a minimal programming language providing tools to automate file management like backups, building process or unit testing.
## Usage
@ -17,11 +17,11 @@ PORTE is a minimal programming language providing tools to automate file managem
```sh
$ porte hello-world.pr
$ prout hello_world.pr
```
## Authors
## Author
- JOLIMAITRE Matthieu <matthieu@imagevo.fr>
- JOLIMAITRE Matthieu <matthieu@imagevo.fr>, developper

View file

@ -1,117 +0,0 @@
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);

View file

@ -1,36 +0,0 @@
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));

View file

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

View file

@ -1,6 +1,3 @@
use std::{env::args, fs};
pub mod demo;
pub mod execution_tree;
pub mod prelude;
pub mod runtime;
@ -8,15 +5,7 @@ pub mod syntax_tree;
pub mod value;
fn main() {
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);
println!("Hello, world!");
}
#[test]
@ -40,11 +29,11 @@ fn it_works() {
]),
),
),
Expr::new_function_call("my_print", vec!["hello, PORTE".into()]),
Expr::new_function_call("my_print", vec!["hello, PROUT".into()]),
Expr::new_function_call("print", vec![Expr::new_variable_call("a")]),
]),
};
let exec = Parser::parse(ast, std_prelude);
let exec = Parser::parse(ast, |builder| std_prelude(builder));
println!("\n\n\n-- running: --");
let _result = Runtime::new().execute(&exec);
}

View file

@ -1,148 +1,14 @@
use std::collections::HashMap;
use crate::{
execution_tree::parser::ParserBuilder,
value::{function::Function, Value},
};
pub fn std_prelude(builder: &mut ParserBuilder) {
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());
}
builder.prelude("print".into(), Function::new_native(1, print).into())
}
fn out(args: Vec<Value>) -> Value {
fn print(args: Vec<Value>) -> Value {
let to_print = args.get(0).unwrap();
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())
println!("{to_print:?}");
Value::Bool(true)
}

View file

@ -24,12 +24,12 @@ impl FrameBuilder {
}
pub fn variable(&mut self, variable_id: &Id, default: Value) {
self.variables.insert(*variable_id, default);
self.variables.insert(variable_id.clone(), 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;
let scope_id = scope_id.clone();
Self {
_scope_id: scope_id,
scope_id,
variables,
}
}
@ -99,12 +99,6 @@ impl Stack {
}
}
impl Default for Stack {
fn default() -> Self {
Self::new()
}
}
pub struct ShortCircuit {
value: Value,
destination_scope_id: Id,
@ -144,9 +138,9 @@ impl ExecReturn {
}
}
impl From<Value> for ExecReturn {
fn from(val: Value) -> Self {
ExecReturn::new_value(val)
impl Into<ExecReturn> for Value {
fn into(self) -> ExecReturn {
ExecReturn::new_value(self)
}
}
@ -175,7 +169,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,
@ -216,7 +210,9 @@ impl Runtime {
ExprInner::FnDef(function_definition) => {
self.execute_function_definition(function_definition)
}
ExprInner::FnCall(function_call) => self.execute_function_call(function_call, program),
ExprInner::FnCall(function_call) => {
self.execute_function_call(function_call, program).into()
}
ExprInner::FnRet(function_return) => {
self.execute_function_return(function_return, program)
}
@ -276,7 +272,7 @@ impl Runtime {
parameter_ids: argument_ids,
body_scope_id,
} = function_definition;
let value = Function::new_constructed(argument_ids.clone(), *body_scope_id);
let value = Function::new_constructed(argument_ids.clone(), body_scope_id.clone());
let value = Value::Function(value);
value.into()
}
@ -371,7 +367,7 @@ impl Runtime {
}) => value,
};
ExecReturn::new_short_circuit(value, *function_scope_id)
ExecReturn::new_short_circuit(value, function_scope_id.clone())
}
pub fn execute_loop(&mut self, loop_: &Loop, program: &Program) -> ExecReturn {
@ -405,7 +401,7 @@ impl Runtime {
}
};
ExecReturn::new_short_circuit(value, *loop_scope_id)
ExecReturn::new_short_circuit(value, loop_scope_id.clone())
}
pub fn execute_condition(&mut self, condition: &Cond, program: &Program) -> ExecReturn {
@ -436,9 +432,3 @@ impl Runtime {
}
}
}
impl Default for Runtime {
fn default() -> Self {
Self::new()
}
}

View file

@ -9,10 +9,6 @@ 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
@ -80,72 +76,6 @@ 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>,
@ -234,5 +164,3 @@ pub struct Cond {
pub arm_true: Expr,
pub arm_false: Option<Expr>,
}
pub mod parser;

View file

@ -1,422 +0,0 @@
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::Id;
use crate::execution_tree::{Expr, Id};
use super::Value;
@ -55,9 +55,9 @@ pub mod function {
}
}
impl From<Function> for Value {
fn from(val: Function) -> Self {
Value::Function(val)
impl Into<Value> for Function {
fn into(self) -> Value {
Value::Function(self)
}
}
}
@ -72,34 +72,6 @@ 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),
@ -132,12 +104,6 @@ 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>,