Skip to content

Commit 9a473f5

Browse files
committed
Add chumsky while exploring parser fixes
1 parent 7f99bc9 commit 9a473f5

File tree

9 files changed

+221
-35
lines changed

9 files changed

+221
-35
lines changed

Cargo.lock

Lines changed: 90 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/fib_acc.tk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env tako
22
// test: all
33

4-
fib_helper(n: T, a: T, b: T, with T: Nat): T=if(n==0, a, fib_helper(n-1, b, a+b))
4+
fib_helper(n: T, a: T, b: T, forall T: Nat): T=if(n==0, a, fib_helper(n-1, b, a+b))
55

66
fib(n: T, forall T: Nat): T=fib_helper(n, 0, 1)
77

takolib/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ llamada = { path = "../llamada", features = [ ] }
5454
better-std = { path = "../better-std", features = [ ] }
5555
entity-component-slab = { path = "../entity-component-slab", features = [ ] }
5656
test-each = "0.2.1"
57+
chumsky = { version = "0.10.1", features = ["pratt"] }
5758

5859
[dev-dependencies]
5960
strum = "0.26.2"

takolib/src/error.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@ impl std::fmt::Display for TError {
2626
f,
2727
"Clang failed with code {return_code} and error message: {error}"
2828
),
29-
ParseError(e) => write!(f, "{e}"),
29+
ParseError(e) => {
30+
write!(f, "{e}")?;
31+
if let Some(location) = e.location() {
32+
write!(f, "at {location:?}")?;
33+
}
34+
Ok(())
35+
}
3036
InternalError { message, location } => {
3137
write!(f, "Internal error: {message}")?;
3238
if let Some(location) = location {

takolib/src/interpreter.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ impl Ctx<'_> {
120120
let (_node_id, lit) = id.get(&self.ast.literals);
121121
let s = self.literals.get_str_by_loc(node.location.start);
122122
Ok(match lit {
123+
Literal::Unit => todo!("{lit:?} {s:?}"),
123124
Literal::Bool => todo!("{lit:?} {s:?}"),
124125
Literal::Numeric => {
125126
Prim::I32(s.expect("Should have string for literal").parse::<i32>()?)

takolib/src/parser/chumsky_test.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
use chumsky::prelude::*;
2+
use chumsky::pratt::*;
3+
use chumsky::extra;
4+
5+
enum Expr {
6+
Add(Box<Self>, Box<Self>),
7+
Sub(Box<Self>, Box<Self>),
8+
Pow(Box<Self>, Box<Self>),
9+
Neg(Box<Self>),
10+
Factorial(Box<Self>),
11+
Deref(Box<Self>),
12+
Literal(i32),
13+
}
14+
15+
impl std::fmt::Display for Expr {
16+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
17+
match self {
18+
Self::Literal(literal) => write!(f, "{literal}"),
19+
Self::Factorial(left) => write!(f, "({left}!)"),
20+
Self::Deref(left) => write!(f, "(*{left})"),
21+
Self::Neg(right) => write!(f, "(-{right})"),
22+
Self::Add(left, right) => write!(f, "({left} + {right})"),
23+
Self::Sub(left, right) => write!(f, "({left} - {right})"),
24+
Self::Pow(left, right) => write!(f, "({left} ^ {right})"),
25+
}
26+
}
27+
}
28+
29+
#[test]
30+
fn chumsky_simple_parser() {
31+
let atom = text::int::<_, extra::Err<Simple<char>>>(10)
32+
.from_str()
33+
.unwrapped()
34+
.map(Expr::Literal)
35+
.padded();
36+
37+
let op = |c| just(c).padded();
38+
39+
let expr = atom.pratt((
40+
// We want factorial to happen before any negation, so we need its precedence to be higher than `Expr::Neg`.
41+
postfix(4, op('!'), |lhs, _, _| Expr::Factorial(Box::new(lhs))),
42+
// Just like in math, we want that if we write -x^2, our parser parses that as -(x^2), so we need it to have
43+
// exponents bind tighter than our prefix operators.
44+
infix(right(3), op('^'), |l, _, r, _| Expr::Pow(Box::new(l), Box::new(r))),
45+
// Notice the conflict with our `Expr::Sub`. This will still parse correctly. We want negation to happen before
46+
// `+` and `-`, so we set its precedence higher.
47+
prefix(2, op('-'), |_, rhs, _| Expr::Neg(Box::new(rhs))),
48+
prefix(2, op('*'), |_, rhs, _| Expr::Deref(Box::new(rhs))),
49+
// Our `-` and `+` bind the weakest, meaning that even if they occur first in an expression, they will be the
50+
// last executed.
51+
infix(left(1), op('+'), |l, _, r, _| Expr::Add(Box::new(l), Box::new(r))),
52+
infix(left(1), op('-'), |l, _, r, _| Expr::Sub(Box::new(l), Box::new(r))),
53+
))
54+
.map(|x| x.to_string());
55+
56+
assert_eq!(
57+
expr.parse("*1 + -2! - -3^2").into_result(),
58+
Ok("(((*1) + (-(2!))) - (-(3 ^ 2)))".to_string()),
59+
);
60+
}

0 commit comments

Comments
 (0)