made the parser work

This commit is contained in:
JOLIMAITRE Matthieu 2022-06-01 03:41:20 +03:00
parent 7aa98bdd04
commit dac8ac1e9c
9 changed files with 588 additions and 112 deletions

2
Cargo.lock generated
View file

@ -85,7 +85,7 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
[[package]]
name = "prout"
version = "0.1.0"
version = "0.2.0"
dependencies = [
"chumsky",
]

View file

@ -1,9 +1,9 @@
[package]
name = "prout"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
chumsky = "0.8.0"
chumsky = "0.8"

View file

@ -17,11 +17,11 @@ PROUT is a minimal programming language providing tools to automate file managem
```sh
$ prout hello_world.pr
$ prout hello-world.pr
```
## Author
## Authors
- JOLIMAITRE Matthieu <matthieu@imagevo.fr>, developper
- JOLIMAITRE Matthieu <matthieu@imagevo.fr>

36
examples/hello-world.pr Normal file
View file

@ -0,0 +1,36 @@
hello: "hello";
prout: "prout";
line: hello;
line <- add(line, " ");
line <- add(line, prout);
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,3 +1,5 @@
use std::{env::args, fs};
pub mod execution_tree;
pub mod prelude;
pub mod runtime;
@ -6,11 +8,14 @@ pub mod value;
fn main() {
use prelude::std_prelude;
let input = todo!();
// let parsed = syntax_tree::parser::Parser::parse(input);
// let executable = execution_tree::parser::Parser::parse(parsed, |b| std_prelude(b));
// let runtime = runtime::Runtime::new();
// runtime.execute(&executable);
let path = args().nth(1).expect("[error]: usage 'prout <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, |b| std_prelude(b));
let mut runtime = runtime::Runtime::new();
runtime.execute(&executable);
}
#[test]

View file

@ -1,14 +1,146 @@
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())
let functions: Vec<(_, _, fn(Vec<Value>) -> Value)> = 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(),
_ => unreachable!(),
}
}
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(),
_ => panic!("comparing different types"),
}
}
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(mut args: Vec<Value>) -> Value {
let name = args.get(1).unwrap().as_string().unwrap().to_string();
let value = args.get(2).unwrap().clone();
let object = args.get_mut(0).unwrap().as_object_mut().unwrap();
object.insert(name, value.clone());
value
}
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)
.map(|value| value.clone())
.unwrap_or_else(|| false.into())
}

View file

@ -29,7 +29,7 @@ impl FrameBuilder {
}
pub struct Frame {
scope_id: Id,
_scope_id: Id,
variables: HashMap<Id, Value>,
}
@ -54,7 +54,7 @@ impl Frame {
let FrameBuilder { variables } = frame_builder;
let scope_id = scope_id.clone();
Self {
scope_id,
_scope_id: scope_id,
variables,
}
}

View file

@ -1,20 +1,47 @@
use chumsky::prelude::*;
use super::*;
use chumsky::{prelude::*, text::whitespace};
pub fn scope_parser() -> impl Parser<char, Scope, Error = Simple<char>> {
expression_parser()
.chain(
just(';')
.padded()
.ignore_then(expression_parser())
.repeated(),
)
.delimited_by(just('{'), just('}'))
.map(|instructions| Scope { instructions })
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 literal_value() -> impl Parser<char, Value, Error = Simple<char>> {
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()
@ -22,105 +49,346 @@ pub fn literal_value() -> impl Parser<char, Value, Error = Simple<char>> {
.chain::<char, _, _>(frac.or_not().flatten())
.collect::<String>()
.from_str()
.unwrapped();
let literal = number.clone().map(|n| Value::Number(n)); // TODO: add other types
.unwrapped()
.map(|n: f64| n.into());
let literal = bool.or(none).or(string).or(number);
literal
}
pub fn name() -> impl Parser<char, String, Error = Simple<char>> {
let name = just('a')
.or(just('b'))
.or(just('c'))
.or(just('d'))
.or(just('e'))
.or(just('f'))
.or(just('g'))
.or(just('h'))
.or(just('i'))
.or(just('j'))
.or(just('k'))
.or(just('l'))
.or(just('m'))
.or(just('n'))
.or(just('o'))
.or(just('p'))
.or(just('q'))
.or(just('r'))
.or(just('s'))
.or(just('t'))
.or(just('u'))
.or(just('v'))
.or(just('w'))
.or(just('x'))
.or(just('y'))
.or(just('z'))
.or(just('-'))
.or(just('_'))
.repeated()
.map(|v| String::from_iter(&v));
#[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-_+/*"));
let name = first.then(rest.repeated()).map(|(f, v)| {
let rest = String::from_iter(&v);
format!("{f}{rest}")
});
name
}
pub fn variable_definition_parser() -> impl Parser<char, VarDef, Error = Simple<char>> {
pub fn variable_definition_parser(
expression: impl AbstractParser<Expr>,
) -> impl AbstractParser<VarDef> {
name()
.padded()
.then_ignore(just(":"))
.padded()
.then(expression_parser())
.then_ignore(just(":").padded())
.then(expression)
.map(|(name, value)| VarDef { name, value })
}
pub fn variable_assignement_parser() -> impl Parser<char, VarAssign, Error = Simpl<char>> {
expression_parser().then() name()
#[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 expression_parser() -> impl Parser<char, Expr, Error = Simple<char>> {
let scope = scope_parser().map(|s| s.into());
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(')')))
.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.clone()).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.clone())
.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(|v| Expr::new_literal(v));
let variable_definition = variable_definition_parser().map(|s| s.into());
let variable_assignment;
let variable_call;
let function_definition;
let function_call;
let function_return;
let loop_;
let loop_break;
let condition;
scope
.or(litteral)
.or(variable_definition)
.or(variable_assignment)
.or(variable_call)
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_call)
.or(function_return)
.or(loop_)
.or(loop_break)
.or(condition)
.or(scope)
.or(litteral)
.or(variable_definition)
.or(variable_assignment)
.or(function_call)
.or(variable_call)
.padded()
});
expression
}
pub fn parser() -> impl Parser<char, Program, Error = Simple<char>> {
let scope = expression_parser().chain(
just(';')
.padded()
.ignore_then(expression_parser())
.repeated(),
);
#[test]
fn test_expression_parser() {
let text = r##"if a b"##;
let parser = expression_parser();
let value = parser.parse(text);
dbg!(value.unwrap());
}
let program = scope
.map(|instructions| {
fn parser() -> impl AbstractParser<Program> {
let scope = expression_parser()
.separated_by(just(';').padded())
.then_ignore(just(';').padded().or_not())
.then_ignore(end());
let program = scope.map(|instructions| {
let body = Scope { instructions };
Program { body }
})
.then_ignore(end().recover_with(skip_then_retry_until([])));
});
program
}
// impl Parser {
// pub fn parse(input: &str) -> Program {
// todo!()
// }
// }
#[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<'i>(&self, input: &'i str) -> Result<Program, Vec<Simple<char>>> {
self.inner.parse(input)
}
}
/*
// example
@ -137,7 +405,7 @@ loop {
if a <= 0 {
break true
} else {
a - 1 -> a
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;
@ -72,6 +72,41 @@ 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_object_mut(&mut self) -> Option<&mut 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),