A minimalist pure functional language implemented via recursive descent parsing.
RDP is a small, educational language parser showcasing how to build a recursive descent parser for a pure functional language. It highlights the core constructs: lambda expressions, let bindings, if-then-else, arithmetic, function application, pattern matching, and function composition.
-
Pure Functional Semantics
Immutability and first-class functions as fundamental concepts. -
Lambda Abstractions
Single-parameter functions using the\x -> expr
syntax. -
Let Bindings
Introduce variables withlet x = ... in ...
. -
Function Application
Apply functions to arguments in an expression-oriented style, e.g.,f x
. -
Function Composition
Combine functions with the.
operator, e.g.,(f . g)
. -
Basic Arithmetic
Support for+
,-
,*
,/
. -
Conditionals
if-then-else
expressions for branching logic. -
Pattern Matching
match expr with | pattern -> expr ...
constructs for branching by comparing patterns (identifiers, numbers, grouped).
.
├── LICENSE.md
├── README.md
├── examples/
│ ├── arithmetic.pfl
│ ├── compose.pfl
│ ├── factorial.pfl
│ ├── higher_order.pfl
│ ├── nested_let.pfl
│ └── precedence.pfl
├── grammar.ebnf
└── src/
├── main.rs
├── ast.rs
├── error.rs
├── lib.rs
├── lexer.rs
├── tokens.rs
└── parser.rs
examples/
Includes sample.pfl
files demonstrating language constructs.grammar.ebnf
Contains the full EBNF grammar specifying the language’s syntax.src/
Source code for the lexer, parser, AST definitions, error handling, and the CLI entry point (main.rs
).
See grammar.ebnf
for the complete specification of RDP’s syntax. It covers expressions like let
, if
, match
, lambda
, arithmetic, logical and comparison operators, function application, and function composition.
let compose = \f -> \g -> \x -> f (g x) in
let double = \x -> x * 2 in
let inc = \x -> x + 1 in
compose double inc 5
- Converts the input string into a series of tokens: keywords (
let
,if
, etc.), operators (+
,-
, etc.), identifiers, and numbers.
- Uses a recursive descent approach, matching each grammar rule with a parsing function.
- Produces an Abstract Syntax Tree (AST) that mirrors the structure of the language.
- Models all expressions:
LetExpr
,IfExpr
,Lambda
,PatternMatch
,Arithmetic
,Logic
,Comparison
,Application
,Term
, etc. - Facilitates subsequent interpretation or optimization stages.
RDP enforces the following precedence (highest to lowest):
- Parentheses (
( ... )
) - Function Application (left-associative)
- Function Composition (
.
operator) - Arithmetic (
+
,-
,*
,/
) - Comparison (
==
,<
,>
) - Logical (
&&
,||
) - Lambda (
\
) - If-Then-Else
- Let-In
RDP provides a CLI tool to parse .pfl
files or inline code.
- Nix: Recommended for a reproducible dev environment.
git clone https://github.com/xosnrdev/rdp.git
cd rdp
nix develop # optional, to enter the dev shell
cargo build --release
-
Parse a File
cargo run --release -- examples/arithmetic.pfl
Prints the AST to
stdout
. -
Parse Source Inline
cargo run --release -- "let x = 10 in x + 5"
-
Example
cargo run --release -- "let compose = \f -> \g -> \x -> f (g x) in compose double inc 5"
When parsing let x = 10 in x + 5
, you might see:
Program {
expression: LetExpr {
identifier: "x",
type_annotation: None,
value: Term(
Number(10.0),
),
body: Arithmetic {
left: Term(
Identifier("x"),
),
operator: Add,
right: Term(
Number(5.0),
),
},
},
}
# Build
cargo build
# Run all tests
cargo test
# Parse a sample .pfl file
cargo run --release -- examples/factorial.pfl
Common demonstrations reside in examples/
:
arithmetic.pfl
Basic arithmetic and grouped expressionscompose.pfl
Composition with the.
operatorfactorial.pfl
Recursive function examplehigher_order.pfl
Functions receiving other functionsnested_let.pfl
Nestedlet
structuresprecedence.pfl
Operator precedence demonstration
- Crafting Interpreters
Inspiration on building interpreters from scratch. - Parsing Techniques
A deep reference for parsing methodologies.
Licensed under MIT. Contributions and forks are welcome!