π³ TypeLang
A tiny language interpreter implemented purely in TypeScript's type-system
Introduction
This is an extremely simplified language interpreter implemented purely in TypeScript type annotations. You pass your code as a string and get back the result by hovering with your mouse on the resulting type annotation.
The syntax is Lisp-like. If you're not familiar with it, here's a quick comparison to JavaScript's syntax:
LISP JavaScript
(add 1 2) add(1, 2)
(subtract 5 2) subtract(5, 2)
(add 3 (subtract 2 1)) add(3, subtract(2, 1))
The language supports booleans, numbers, strings, conditionals (if statements), and calling the following built-in functions:
++
: Increases a number by one.--
: Decreases a number by one.Eq
: Checks if both of its arguments are equal.And
: Returnstrue
if both of its arguments aretrue
.Or
: Returnstrue
if at least one of its arguments aretrue
.Join
: Concatenates two strings together.
It also supports declaring variables and functions (See bellow).
See it live)
Try running the code (Install typelang
with npm install typelang
or with yarn install typelang
(requires TypeScript v4.1.0 or above).
Primitives
The language has support for nulls (no value), numbers, strings, and booleans.
import { Eval } from "typelang";
type Result = Eval<''>; // null
type Result = Eval<'123'>; // '123'
type Result = Eval<'"hello"'>; // 'hello'
type Result = Eval<'True'>; // true
type Result = Eval<'False'>; // false
Conditionals
If statements are in the form of:
(If predicate then-expression else-expression)
import { Eval } from "typelang";
type Result = Eval<'(If True "yes" "no")'>; // 'yes'
type Result = Eval<'(If False "yes" "no")'>; // 'no'
Calling functions
The language supports a few built-in functions (listed above) and you can also define custom functions.
Here's how you call a function:
(function-name arg1 arg2 arg3 ...)
import { Eval } from "typelang";
type Result = Eval<'(Join "a" "b" "c" "d")'>; // 'abcd'
type Result = Eval<'(Eq 2 2)'>; // true
type Result = Eval<'(Eq "you" "me")'>; // false
type Result = Eval<'(And True True)'>; // true
type Result = Eval<'(And False False)'>; // false
type Result = Eval<'(Or True False)'>; // true
type Result = Eval<'(Or False True)'>; // true
type Result = Eval<'(++ 2)'>; // '3'
type Result = Eval<'(-- 5)'>; // '4'
Declaring variables
Declare a variable to hold a value and reference it later on. Here's how you do it:
(Def variable-name value)
Notice that the language returns the last expression, so the first example declares a variable and then returns it:
import { Eval } from "typelang";
type Result = Eval<'(Def x 1) x'>; // '1'
type Result = Eval<'undefined_variable'>; // null
type Result = Eval<'(Def x 2) (++ x)'>; // '3'
type Result = Eval<`
(Def x (++ 3))
(Def y (++ x))
(Join "result: " y)
`>; // 'result: 5'
Declaring functions
To declare a custom function, use the following syntax:
(Fun function-name (arg1 arg2) (function-body))
Note that you can access arg1
and arg2
only from inside the function body and not from outside. You can still access variables declared on the global scope from inside the function scope.
import { Eval } from "typelang";
type Result = Eval<`
(Fun SayHello (first last) (Join "Hello " first " " last))
(SayHello "John" "Doe")
`>; // 'Hello John Doe'
The last expression is returned
Notice that all expressions are evaluated but only the last one is returned:
import { Eval } from "typelang";
type Result = Eval<'(++ 1) (++ 2)'>; // '3'
type Result = Eval<'(Eq 1 1) (Eq 2 3)'>; // false
never
Invalid syntax returns In case of an error, Eval
returns never
:
import { Eval } from "typelang";
type Result = Eval<'(++ (++ '>; // never
type Result = Eval<') ++'>; // never
type Result = Eval<'"aa'>; // never
Note: TypeScript has a limitation on how deep its computation can get. Because of this, we're limited to small inputs. If you're getting the following error: Type instantiation is excessively deep and possibly infinite
, please try using a smaller input.