This is an example project for implementing a basic interpreter-slash-transpiler from a simple toy language to Python:
$ python3 -m compyle --show-interpret --show-transpyle ' a := 3 # assign >>> (a / 2:3) * (4 * 12) # evaluate '
The pyparsing
and attrs
libraries are required.
The Toy Language itself consists of expressions, which are:
- References to names, such as
foo
, - Literals of integers, such as
1337
, - Literals of fractions, such as
13 : 37
fractions or13.37
decimals, - Binary operators
+
,-
,/
and*
, such as(foo * 13.37)
.
Parentheses are mandatory around each binary operator expression but disallowed anywhere else.
The Toy Language Interpreter uses statements incorporating Toy Language expressions, which are:
- Assignments to names, such as
foo := 1337
, - Evaluations of expressions, such as
>>> (foo / 20)
.
Statements are line-separated. A Toy Language Program is a sequence of statements, such as:
foo := 2 : 3 bar := foo / 3 >>> bar foo := 2 >>> bar * 2
A trailing #
on a statement indicates comments.
The #
and the rest of the line are ignored.
The basic functionality of compyle
is based on three Expression actions:
- Evaluating means deriving a (Python) value for an expression.
For example,
3 : 2
can be evaluated tofractions.Fraction(3, 2)
. - Specialising means updating/simplifying an expression with more information.
For example,
(a * 3)
can be specialised witha := 3
to9
. - Transpyling means converting an Expression to Python source code.
For example,
(a * 3)
van be transpylied to__namespace__["a"].evaluate() * 3
.
The compyle
setup is roughly divided into three sections:
- The abstract expression/statement setup, defining how expressions are handled:
- transpyle.py defines expressions, how they are specialized, transpyled and evaluated.
- interpret.py defines statements, how they are processed and evaluated.
- The concrete object setup, defining what expressions can express:
- variables.py defines references and how to translate (Python) values back to expressions. This allows evaluating expressions and references to specialise them.
- numbers.py defines the expressions for Integers and Fractions. There are no floats, as "float literals" are translated to Fractions.
- operator.py defines basic arithmetic operators
- The client setup, defining how to write expressions and statements:
- parser.py defines parsing of Toy Language to expressions and statements.
- frontend.py defines the command line interface.
There are a couple of restrictions from the design of compyle
.
These could be lifted, but would complicate understanding of the code.
- Expressions represent all levels of AST, values and transpiled code.
- Scoping/Namespaces are not first-class, but tied to Expressions.
- Specialisation is all-or-nothing and eager.
- Operators are untyped and always follow the same rules.
- The transpilation target is plain Python source code.