Home

Awesome

🌳 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:

It also supports declaring variables and functions (See bellow).

Try running the code (See it live)

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

Invalid syntax returns never

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.

Additional links