Hello! Oak is a parser generator based on Parsing Expression Grammar (PEG). This project has been started to explore the idea of typing parsing expressions. It is written as a syntax extension and can be embedded in your Rust code without complicating the build system.
Independently of your programming experience with parser generators, a first step is to consult the Getting Started section. If you are new to parser generator or PEG, the section Learn Oak is a smooth tutorial to Oak for incrementally building a small language with arithmetic expressions and variable bindings. The section Syntax and Semantics gives a short summary of the Oak constructions. For more informal and in-depth discussion about the design rational of Oak and to find out why it is different from other parser generators, please consult The Story of Oak.
The code is available on github.
e is a sub expression and T is the type of e. The types are only informative, it does not show unit propagation. Greedy operators do not generate "backtracking points" and consume as many characters as possible.
| Expression | Type | Precedence level | Description |
|---|---|---|---|
"literal" |
() |
0 | Match a string literal. |
. |
char |
0 | Match any single character. |
["a-zA-Z-"] |
char |
0 | Match a character from one of the specified classes. |
(e) |
T |
0 | Group an expression. |
ident |
Type of rule ident |
0 | Call the rule with the name ident. |
e? |
Option<T> |
1 | (Greedy) Match zero or one e. Always succeed. |
e* |
Vec<T> |
1 | (Greedy) Match zero or more e. Always succeed. |
e+ |
Vec<T> |
1 | (Greedy) Match one or more e. |
&e |
(^) |
2 | Try to match e and succeed if e succeeds. It does not consume any input. |
!e |
(^) |
2 | Try to match e and succeed if e fails. It does not consume any input. |
e1 e2 e3 |
(T1, T2, T3) |
3 | Match e1 e2 e3 in sequence. Immediately fails when one fails. |
e > f |
Return type of f |
4 | Match e and if it succeeds, call f(v) where v is the value of e. |
e -> () |
() |
4 | Force the type of e to be (). |
e -> (^) |
(^) |
4 | Force the type of e to be (^). |
e1 / e2 / e3 |
Type of any e |
5 | Match e1 e2 e3 in sequence. Immediately succeeds when one succeeds. |
My goal is to propose a complete library to ease the development of Embedded Domain Specific Language (EDSL) in Rust with procedural macros. For the moment my priority is to stabilize, test and finish this manual. Next I want to add more static analysis to prevent grammar design error such as in "=" / "==" (can you find what is wrong?) Here some other wanted features:
Spanned<T> structure to get location information (#13).e1 > A / e2 > B with A and B being variants (#41, #53, #54).A shortcoming to cleanly achieve these objectives with the Rust compiler is that we can only access item definitions declared inside the procedural macro. It probably means that, for the moment, compositionality would come at the cost of some run-time verifications (or no inter-grammar analysis at all).