Skip to content

Internal Compiler API

Andrew Johnson edited this page Jun 10, 2025 · 51 revisions

If you want to write a plugin for LM, then it may be necessary to interface with compiler objects. These APIs presented here are stable and will be supported by their nominal type signature.

If something is not present here in the type signature, then it may not be stable. For example, destructuring types used in these functions may be unstable.

Type Definitions

Types here should be treated as opaque, even if they are not defined as explicitly opaque.

type AST;
type Type;
type TypeContext;
type SourceLocation;

Global Indexes

Global indexes are accessible through helper functions.

# var-to-def-index permits lookup from variable reference to variable definition
#
# a variable reference will resolve to either
#    a local variable: finding the original local binding definition
#    a global variable: finding the original global binding definition
#
let var-to-def(var: AST): AST;

Apply

Simple function application can be fairly complicated when considering all of the various inference rules present in the type system. The "apply" set of functions take function types and argument types to produce return types, providing a unified interface for inference.

let apply(functions-type: Type, arguments-type: Type): Type;

Type Constructors

Ground types have short-hand constructors.

# Ground Types
let t1(tag: CString, p1: Type): Type;
let t2(tag: CString, p1: Type, p2: Type): Type;
let t3(tag: CString, p1: Type, p2: Type, p3: Type): Type;

# Type Variables
let tv(tag: CString): Type;

# Top Type
let ta: Type;

# Conjunction
let $"&&"(lt: Type, rt: Type): Type;

Type Destructors

Ground types have short-hand destructors.

# Get type parameter, starting from left (l1) moving left (l2), (l3), ...
let .l1(tt: Type): Type; # T<a,b,c,d> .l1 = a
let .l2(tt: Type): Type; # T<a,b,c,d> .l2 = b
let .l3(tt: Type): Type; # T<a,b,c,d> .l3 = c
let .l4(tt: Type): Type; # T<a,b,c,d> .l4 = d
let .l(tt: Type, i: USize): Type; # ...

# Get type parameter, starting from right (r1) moving left (r2), (r3), ...
let .r1(tt: Type): Type; # T<a,b,c,d> .r1 = d
let .r2(tt: Type): Type; # T<a,b,c,d> .r2 = c
let .r3(tt: Type): Type; # T<a,b,c,d> .r3 = b
let .r4(tt: Type): Type; # T<a,b,c,d> .r4 = a
let .r(tt: Type, i: USize): Type; # ...

Type Normalization

Types have a normal form with minimal inferable information, and a denormal form with maximal inferable information. Super-normal form refers to the type of a left-hand-side binding such as a variable, which may discard some information.

let normalize(tt: Type): Type;
let denormalize(tt: Type): Type;
let supernormalize(tt: Type): Type;

Unification

Unification is a process by which type information is compared or merged. can-unify is much faster than unify.

let can-unify(supertype: Type, subtype: Type): Bool;
let unify(supertype: Type, subtype: Type): Maybe<TypeContext>;

AST Constructors

The AST is starting to stabilize.

let mk-lit(val: CString): AST;
let mk-lit(val: SmartString): AST;

let mk-var(val: CString): AST;
let mk-var(val: SmartString): AST;

let mk-atype(tt: Type): AST;
let .ascript(t: AST, tt: Type): AST;

let mk-app(l: AST, r: AST): AST;
let mk-cons(l: AST, r: AST): AST;

let .with-location(t: AST, loc: SourceLocation): AST;

Conveniences

These methods are provided for common convenience.

let .into(t: AST, tgt: Type<String>): String;
let .into(tt: Type, tgt: Type<String>): String;

Conventions

The LM Core project has strict coding conventions that we try to follow to create consistency within the project.

Full Words > Abbreviations

Full descriptive language is preferred over abbreviations whenever possible: TypeContext instead of TContext.

Plural > Singular

Singular noun form is preferred over plural form: mk-thing instead of mk-things.

Simple Present Tense, Base Form

Simple present tense is preferred over other verb tenses: write, read, identify, normalize.

Methods > Functions

Only use functions when a new thing is created; if the thing is being manipulated then use a method.

Constructors

Constructors use functional language naming conventions mk-thing.

Data Structures Are Private

Internal data structures should never be exposed directly through the API.

Clone this wiki locally