This little program's Welcome message says it all:
REPL for our demo 'TinyCalc' language, consisting only of: float operands, parens and the 4 most basic arithmetic operators (with no precedence: use parens). Enter: · Q to quit · A to toggle between: · "ADT" interpreter approach (default) · "Alt" interpreter approach · <expr> to parse-and-prettyprint-and-eval
A self-contained (stdlib-only, no deps) interactive Read-Eval-Print-Loop (REPL) of a minimalist tiny uni-typed language with multiple (principally / architecturally "pluggable") interpreters.
That is: a minimal working skeleton for developing (in Go) custom languages to be lexed-parsed-and-interpreted.
Two interpreters are built-in:
- eval — arithmetic reduction of parsed expression tree to final numeric result
- pretty-print — string-formatting of parsed expression tree
(Other "interpreters" —more so in the general case, less so for the mini-language at hand— could be optimizers, simplifiers, byte-code generators, transpilers, compilers etc..)
Furthermore, two approaches to interpretation of the syntax-tree (each sporting its own eval and own pretty-print implementation) are included:
approach-adt-interp.go— used by default and probably the more intuitive, idiomatic, common approach — also in retrospect, at least in Go, the terser and more comprehensible one;
approach-alt-interp.go— inspired by http://okmij.org/ftp/tagless-final/course/lecture.pdf (chapter 2 only) — while this approach would be much more desirable in a language such as Haskell (and when targeting embedded DSLs), in Go (and targeting lexed-and-parsed instead of embedded languages) it soon necessitates tedious-to-read-and-write code bloat — as it turns out, much of the "almost-magic" convenience of tagless-final is truly afforded by the expressive power of Haskell's type-classes and parametric polymorphism, and so to transfer the idea to a very-low-level language would amount to furnishing a code-generator not far in its capabilities from a Haskell compiler! Not on, for now.