Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/gustavodiasag/simpler
Interpreter for a functional, expression-based programming language
https://github.com/gustavodiasag/simpler
functional-programming interpreter lambda-calculus ocaml programming-language
Last synced: about 1 month ago
JSON representation
Interpreter for a functional, expression-based programming language
- Host: GitHub
- URL: https://github.com/gustavodiasag/simpler
- Owner: gustavodiasag
- License: mit
- Created: 2023-12-21T21:37:34.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2024-05-09T00:45:06.000Z (9 months ago)
- Last Synced: 2024-05-09T01:44:45.041Z (9 months ago)
- Topics: functional-programming, interpreter, lambda-calculus, ocaml, programming-language
- Language: OCaml
- Homepage:
- Size: 79.1 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# SimPLer
[![SimPLer](https://github.com/gustavodiasag/SimPLer/actions/workflows/test.yml/badge.svg)](https://github.com/gustavodiasag/SimPLer/actions/workflows/test.yml)
Implementation of an interpreter for the SimPL and Core OCaml programming languages as the final exercise from the book [OCaml Programming: Correct + Efficient + Beautiful](https://cs3110.github.io/textbook/ocaml_programming.pdf).
Although these toy languages are just basic calculators in some way (given that only expressions are supported), some ideas present in [lambda calculus theory](https://plato.stanford.edu/entries/lambda-calculus/) are provided by them, such as substitution and anonymous functions. Besides that, the languages are defined with syntactic and semantic rules very similar to those of OCaml.
Important excerpts from the book regarding the theoretical and mathematical aspects of the project are separately described in a [notes section](NOTES.md).
# Specifications
## Syntax
The syntax rules in the standard BNF notation for SimPL and Core Ocaml are presented below:
### SimPL
```
e ::= x
| i
| b
| e binop e'
| if e then e' else e''
| let x = e in e'binop ::= + | * | <=
x ::=
i ::=
b ::= true | false
```### Core OCaml
```
e ::= x
| e1 e2
| fun x -> e
| i
| b
| e1 bop e2
| (e1, e2)
| fst e
| snd e
| if e1 then e2 else e3
| let x = e1 in e2bop ::= + | * | < | > | = | <= | >=
x ::=
i ::=
b ::= true | false
v ::= fun x -> e | i | b | (v1, v2)
```## Lexing and Parsing
Neither the lexer nor the parser for the language are developed from scratch. Instead, the implementation relies on tools provided by the libraries [ocamllex](https://v2.ocaml.org/manual/lexyacc.html), responsible for the generation of lexical analyzers, and [Menhir](https://gallium.inria.fr/~fpottier/menhir/manual.pdf), responsible for the generation of parsers.
The details for the lexer definition (i.e., identifiers and rules) are found in [lexer.mll](lib/lexer.mll), and the details for the grammar definition (i.e., symbols and production rules) are found in [parser.mly](lib/parser.mly).
## Evaluation
The process of simplifying the languages' [Abstract Syntax Tree](lib/ast.ml) down to a single value is defined through a mathematical relation whose style is known as **operational semantics**. More specifically, these semantics are divided in small step and big step semantics, where:
- **Small step** semantics represent execution in terms of individual small steps, or how a program takes one single step of execution.
- **Big step** semantics represent execution in terms of a big step from an expression directly to a value, abstracting away all the details of single steps.
Both styles are discussed by the book, but since big-step semantics are more similar to how an interpreter would actually be implemented, the languages' evaluation strategy is based only on this model.
## Substitution
The implementation of variables use the **substitution model** of evaluation, where the value of a variable is substituted for its name throughout the scope of that name, as soon as a binding of the variable is found.
For SimPL (which is composed of expressions), the notation `e'{e/x}` is used to determine the expression `e'` with `e` substituted for `x`. So anywhere `x` appears in `e'`, it should be replaced with `e`. For Core OCaml, the same logic is applied, but since it supports a value abstraction, the notation changes slightly to `e{v/x}`. This model is defined for each of the languages' constructs, but given that Core OCaml is a superset of SimPL, only its rules are specified:
### Constants
Variables cannot appear in them (e.g., `x` cannot syntactically occur in `42`), so substitution leaves constants unchanged.
```
i{v/x} = ib{v/x} = b
```### Operators and conditionals
All that substitution does is recurse inside the subexpressions.
```
(e1 + e2){v/x} = e1{v/x} + e2{v/x}(if e1 then e2 else e3){v/x} = if e1{v/x} then e2{v/x} else e3{v/x}
```### Variables
There are two possibilities, either the variable to be substituted is found or not, in which case the substitution must not happen
```
x{v/x} = vy{v/x} = y
```### Let expressions
Two cases are also possible, depending on the name of the bound variable. If a shadowed name has been reached, substitution must stop in order to prioritize the most recent binding, so it is only applied to the first expression. On the contrary, substitution carefully recurses inside both expressions, avoiding [capturing any variables](NOTES.md/#capture-avoiding-substitution-1139).
```
(let x = e1 in e2){v/x} = let x = e1{v/x} in e2(let y = e1 in e2){v/x} = let y = e1{v/x} in e2{v/x}
if y not in FV(v)
```### Anonymous functions
Fundamentally the same as for "let" expressions.
```
(fun x -> e'){v/x} = (fun x -> e')(fun y -> e'){v/x} = (fun y -> e'{v/x})
if y not in FV(v)
```### Tuples
```
(e1, e2){v/x} = (e1{v/x}, e2{v/x})(fst e){v/x} = fst (e{v/x})
(snd e){v/x} = snd (e{v/x})
```# License
The project is licensed under the [MIT License](LICENSE)