add block expressions
This commit is contained in:
parent
c5b045ebcb
commit
3c184a1ac8
8 changed files with 129 additions and 54 deletions
30
README.md
30
README.md
|
|
@ -33,3 +33,33 @@ Found 2 outliers among 100 measurements (2.00%)
|
|||
1 (1.00%) high mild
|
||||
1 (1.00%) high severe
|
||||
```
|
||||
|
||||
- adding block expression to avoid deep recursion (22.8 MB/s parsing, also enabled running long programs without stack overflow lol)
|
||||
```
|
||||
Fibonacci/Parse/programs/medium.jang
|
||||
time: [9.3838 ms 9.3884 ms 9.3929 ms]
|
||||
change: [−31.880% −31.709% −31.544%] (p = 0.00 < 0.05)
|
||||
Performance has improved.
|
||||
Found 10 outliers among 100 measurements (10.00%)
|
||||
5 (5.00%) low severe
|
||||
5 (5.00%) high mild
|
||||
Fibonacci/Interpret/programs/medium.jang
|
||||
time: [11.324 ms 11.329 ms 11.334 ms]
|
||||
change: [−27.811% −27.695% −27.582%] (p = 0.00 < 0.05)
|
||||
Performance has improved.
|
||||
Found 4 outliers among 100 measurements (4.00%)
|
||||
3 (3.00%) high mild
|
||||
1 (1.00%) high severe
|
||||
Fibonacci/Parse/programs/test.jang
|
||||
time: [3.0235 µs 3.2381 µs 3.4928 µs]
|
||||
change: [−23.214% −13.984% −5.8612%] (p = 0.00 < 0.05)
|
||||
Performance has improved.
|
||||
Fibonacci/Interpret/programs/test.jang
|
||||
time: [2.5598 µs 2.7237 µs 2.8978 µs]
|
||||
change: [−59.249% −57.809% −55.973%] (p = 0.00 < 0.05)
|
||||
Performance has improved.
|
||||
Found 19 outliers among 100 measurements (19.00%)
|
||||
4 (4.00%) low mild
|
||||
7 (7.00%) high mild
|
||||
8 (8.00%) high severe
|
||||
```
|
||||
|
|
|
|||
|
|
@ -4,8 +4,14 @@ use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
|
|||
use jang::*;
|
||||
|
||||
fn bench_parsers(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("Fibonacci");
|
||||
for input_path in ["programs/medium.jang", "programs/test.jang"].iter() {
|
||||
let mut group = c.benchmark_group("jang");
|
||||
for input_path in [
|
||||
"programs/long.jang",
|
||||
"programs/medium.jang",
|
||||
"programs/test.jang",
|
||||
]
|
||||
.iter()
|
||||
{
|
||||
let src = std::fs::read_to_string(input_path).unwrap();
|
||||
|
||||
group.bench_with_input(BenchmarkId::new("Parse", input_path), &src, |b, src| {
|
||||
|
|
@ -17,7 +23,8 @@ fn bench_parsers(c: &mut Criterion) {
|
|||
return;
|
||||
};
|
||||
let mut vars = HashMap::new();
|
||||
_ = eval(&ast, &mut vars);
|
||||
let mut scope = Vec::new();
|
||||
_ = eval(&ast, &mut vars, &mut scope);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,24 +18,32 @@ pub enum IdentValue<'src> {
|
|||
pub fn eval<'src>(
|
||||
expr: &Expr<'src>,
|
||||
vars: &mut HashMap<&'src str, Vec<IdentValue<'src>>>,
|
||||
scope_vars: &mut Vec<&'src str>,
|
||||
) -> Result<f64, String> {
|
||||
fn bind_var<'src>(
|
||||
vars: &mut HashMap<&'src str, Vec<IdentValue<'src>>>,
|
||||
scope_vars: &mut Vec<&'src str>,
|
||||
name: &'src str,
|
||||
val: IdentValue<'src>,
|
||||
) {
|
||||
vars.entry(name).or_insert(Vec::new()).push(val);
|
||||
scope_vars.push(name);
|
||||
}
|
||||
fn unbind_var<'src>(vars: &mut HashMap<&'src str, Vec<IdentValue<'src>>>, name: &'src str) {
|
||||
vars.get_mut(name).unwrap().pop();
|
||||
fn drop_scope<'src>(
|
||||
vars: &mut HashMap<&'src str, Vec<IdentValue<'src>>>,
|
||||
scope_vars: Vec<&'src str>,
|
||||
) {
|
||||
for var in scope_vars.into_iter() {
|
||||
vars.get_mut(var).unwrap().pop();
|
||||
}
|
||||
}
|
||||
match expr {
|
||||
Expr::Num(x) => Ok(*x),
|
||||
Expr::Neg(a) => Ok(-eval(a, vars)?),
|
||||
Expr::Add(a, b) => Ok(eval(a, vars)? + eval(b, vars)?),
|
||||
Expr::Sub(a, b) => Ok(eval(a, vars)? - eval(b, vars)?),
|
||||
Expr::Mul(a, b) => Ok(eval(a, vars)? * eval(b, vars)?),
|
||||
Expr::Div(a, b) => Ok(eval(a, vars)? / eval(b, vars)?),
|
||||
Expr::Neg(a) => Ok(-eval(a, vars, scope_vars)?),
|
||||
Expr::Add(a, b) => Ok(eval(a, vars, scope_vars)? + eval(b, vars, scope_vars)?),
|
||||
Expr::Sub(a, b) => Ok(eval(a, vars, scope_vars)? - eval(b, vars, scope_vars)?),
|
||||
Expr::Mul(a, b) => Ok(eval(a, vars, scope_vars)? * eval(b, vars, scope_vars)?),
|
||||
Expr::Div(a, b) => Ok(eval(a, vars, scope_vars)? / eval(b, vars, scope_vars)?),
|
||||
Expr::Var(name) => {
|
||||
let ident_value = vars.get(name).and_then(|v| v.last());
|
||||
match ident_value {
|
||||
|
|
@ -44,12 +52,10 @@ pub fn eval<'src>(
|
|||
None => Err(format!("Undefined variable `{}`", name)),
|
||||
}
|
||||
}
|
||||
Expr::Let { name, rhs, then } => {
|
||||
let val = eval(rhs, vars)?;
|
||||
bind_var(vars, name, IdentValue::Var(val));
|
||||
let output = eval(then, vars);
|
||||
unbind_var(vars, name);
|
||||
output
|
||||
Expr::Let { name, rhs } => {
|
||||
let val = eval(rhs, vars, scope_vars)?;
|
||||
bind_var(vars, scope_vars, name, IdentValue::Var(val));
|
||||
Ok(val)
|
||||
}
|
||||
Expr::Call(name, args) => {
|
||||
let ident_value = vars.get(name).and_then(|v| v.last()).cloned();
|
||||
|
|
@ -66,15 +72,19 @@ pub fn eval<'src>(
|
|||
let binds = params
|
||||
.iter()
|
||||
.zip(args)
|
||||
.map(|(param, arg)| (param, eval(arg, vars)))
|
||||
.map(|(param, arg)| (param, eval(arg, vars, scope_vars)))
|
||||
.collect::<Vec<_>>();
|
||||
let mut param_scope = Vec::with_capacity(binds.len());
|
||||
for (param, bind) in binds {
|
||||
bind_var(vars, param, IdentValue::Var(bind.unwrap()));
|
||||
}
|
||||
let return_value = eval(&body, vars);
|
||||
for param in params.iter() {
|
||||
unbind_var(vars, param);
|
||||
bind_var(
|
||||
vars,
|
||||
&mut param_scope,
|
||||
param,
|
||||
IdentValue::Var(bind.unwrap()),
|
||||
);
|
||||
}
|
||||
let return_value = eval(&body, vars, scope_vars);
|
||||
drop_scope(vars, param_scope);
|
||||
return_value
|
||||
}
|
||||
}
|
||||
|
|
@ -84,12 +94,7 @@ pub fn eval<'src>(
|
|||
None => Err(format!("Undefined variable `{}`", name)),
|
||||
}
|
||||
}
|
||||
Expr::Fn {
|
||||
name,
|
||||
args,
|
||||
body,
|
||||
then,
|
||||
} => {
|
||||
Expr::Fn { name, args, body } => {
|
||||
vars.entry(name)
|
||||
.or_insert(Vec::new())
|
||||
.push(IdentValue::Func {
|
||||
|
|
@ -97,11 +102,25 @@ pub fn eval<'src>(
|
|||
params: args.clone(),
|
||||
body: body.clone(),
|
||||
});
|
||||
let output = eval(then, vars);
|
||||
vars.get_mut(name).unwrap().pop();
|
||||
output
|
||||
// todo - return value
|
||||
Ok(0.0)
|
||||
}
|
||||
Expr::Block(exprs) => {
|
||||
// todo - unbind vars after block
|
||||
let mut return_value = Err(String::new());
|
||||
let mut scope = Vec::new();
|
||||
for expr in exprs.iter() {
|
||||
return_value = eval(expr, vars, &mut scope);
|
||||
}
|
||||
drop_scope(vars, scope);
|
||||
return_value.map_err(|err| {
|
||||
if err.is_empty() {
|
||||
"block must return a value".to_string()
|
||||
} else {
|
||||
err
|
||||
}
|
||||
})
|
||||
}
|
||||
Expr::Block(_) => panic!("unimplemented"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -135,7 +154,7 @@ pub enum Token<'src> {
|
|||
Comma,
|
||||
#[token("=")]
|
||||
Equals,
|
||||
#[regex("[0-9]", |lex| lex.slice().parse::<u64>().unwrap())]
|
||||
#[regex("[0-9]+", |lex| lex.slice().parse::<u64>().unwrap())]
|
||||
Number(u64),
|
||||
#[regex("[_a-zA-Z][_a-zA-Z0-9]*", |lex| lex.slice())]
|
||||
Ident(&'src str),
|
||||
|
|
@ -157,13 +176,11 @@ pub enum Expr<'src> {
|
|||
Let {
|
||||
name: &'src str,
|
||||
rhs: Box<Expr<'src>>,
|
||||
then: Box<Expr<'src>>,
|
||||
},
|
||||
Fn {
|
||||
name: &'src str,
|
||||
args: Vec<&'src str>,
|
||||
body: Rc<Expr<'src>>,
|
||||
then: Box<Expr<'src>>,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -194,12 +211,23 @@ pub fn parse<'src, const LOG: bool>(
|
|||
}
|
||||
(t, s)
|
||||
});
|
||||
|
||||
create_parser().parse(token_stream).into_result()
|
||||
}
|
||||
|
||||
pub fn create_parser<'src, I: ValueInput<'src, Token = Token<'src>, Span = SimpleSpan>>()
|
||||
-> impl Parser<'src, I, Expr<'src>, ParseError<'src>> {
|
||||
// decl => fn | let | block
|
||||
// block => { stmt_list }
|
||||
// stmt_list = decl1; decl2; decl3|expr
|
||||
// let => let ident = expr; decl
|
||||
// fn => fn f x y = expr; decl
|
||||
// expr => sum | block
|
||||
// sum => product +/- product
|
||||
// product => unary * / unary
|
||||
// unary => -atom
|
||||
// atom => (expr) | call | ident
|
||||
// call => ident(arg1,arg2)
|
||||
|
||||
let ident = any()
|
||||
.filter(|tok| matches!(tok, Token::Ident(_)))
|
||||
.map(|tok| match tok {
|
||||
|
|
@ -213,6 +241,8 @@ pub fn create_parser<'src, I: ValueInput<'src, Token = Token<'src>, Span = Simpl
|
|||
_ => unreachable!("alredy filtered on number"),
|
||||
});
|
||||
|
||||
let mut block = Recursive::declare();
|
||||
|
||||
let expr = recursive(|expr| {
|
||||
let call = ident
|
||||
.then(
|
||||
|
|
@ -253,21 +283,18 @@ pub fn create_parser<'src, I: ValueInput<'src, Token = Token<'src>, Span = Simpl
|
|||
|lhs, (op, rhs)| op(Box::new(lhs), Box::new(rhs)),
|
||||
);
|
||||
|
||||
sum
|
||||
block.clone().or(sum)
|
||||
});
|
||||
|
||||
let decl = recursive(|decl| {
|
||||
let decl = recursive(|_decl| {
|
||||
// ex: let x = 5*2
|
||||
let decl_let = just(Token::KWLet)
|
||||
.ignore_then(ident)
|
||||
.then_ignore(just(Token::Equals))
|
||||
.then(expr.clone())
|
||||
.then_ignore(just(Token::Semicolon))
|
||||
.then(decl.clone())
|
||||
.map(|((name, rhs), then)| Expr::Let {
|
||||
.map(|(name, rhs)| Expr::Let {
|
||||
name,
|
||||
rhs: Box::new(rhs),
|
||||
then: Box::new(then),
|
||||
});
|
||||
|
||||
// ex: fn f x y = x + y
|
||||
|
|
@ -277,24 +304,24 @@ pub fn create_parser<'src, I: ValueInput<'src, Token = Token<'src>, Span = Simpl
|
|||
.then(ident.repeated().collect::<Vec<_>>())
|
||||
.then_ignore(just(Token::Equals))
|
||||
.then(expr.clone())
|
||||
.then_ignore(just(Token::Semicolon))
|
||||
.then(decl.clone())
|
||||
.map(|(((name, params), body), then)| Expr::Fn {
|
||||
.map(|((name, params), body)| Expr::Fn {
|
||||
name,
|
||||
args: params,
|
||||
body: Rc::new(body),
|
||||
then: Box::new(then),
|
||||
});
|
||||
|
||||
// let decl_block = decl
|
||||
// .repeated()
|
||||
// .collect::<Vec<_>>()
|
||||
// .delimited_by(just(Token::LCurly), just(Token::RCurly))
|
||||
// .map(Expr::Block);
|
||||
|
||||
// must parse 'let' keyword first so it doesn't become an identifier
|
||||
decl_fn.or(decl_let).or(expr)
|
||||
decl_fn.or(decl_let).or(expr.clone())
|
||||
});
|
||||
|
||||
block.define(
|
||||
decl.clone()
|
||||
.separated_by(just(Token::Semicolon))
|
||||
.allow_trailing()
|
||||
.collect::<Vec<_>>()
|
||||
.delimited_by(just(Token::LCurly), just(Token::RCurly))
|
||||
.map(Expr::Block),
|
||||
);
|
||||
|
||||
decl
|
||||
}
|
||||
|
|
|
|||
4
programs/blocks.jang
Normal file
4
programs/blocks.jang
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
let x = {
|
||||
let y = 10;
|
||||
5 + y
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
{
|
||||
let five = 5;
|
||||
let five = five + 3;
|
||||
fn sum x y = x + y;
|
||||
|
|
@ -999999,3 +1000000,4 @@ fn sum x y = x + y;
|
|||
fn mul x y = x*y;
|
||||
let six = sum(1,sum(mul(2,2),3));
|
||||
six
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
{
|
||||
let five = 5;
|
||||
let five = five + 3;
|
||||
fn sum x y = x + y;
|
||||
|
|
@ -9999,3 +10000,4 @@ fn sum x y = x + y;
|
|||
fn mul x y = x*y;
|
||||
let six = sum(1,sum(mul(2,2),3));
|
||||
six
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
{
|
||||
let five = 5;
|
||||
let five = five + 3;
|
||||
fn sum x y = x + y;
|
||||
|
|
@ -904,3 +905,4 @@ fn sum x y = x + y;
|
|||
fn mul x y = x*y;
|
||||
let six = sum(1,sum(mul(2,2),3));
|
||||
six
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ fn main() {
|
|||
println!("parsed:\n{:?}\n---", parse_result);
|
||||
if let Ok(ast) = parse_result {
|
||||
let mut vars = HashMap::new();
|
||||
let output = eval(&ast, &mut vars);
|
||||
let mut scope = Vec::new();
|
||||
let output = eval(&ast, &mut vars, &mut scope);
|
||||
println!("{:?}", output);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue