You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
172 lines
3.4 KiB
TypeScript
172 lines
3.4 KiB
TypeScript
import type { Value } from './types';
|
|
|
|
|
|
// Literals and Variables
|
|
|
|
export type Literal = {
|
|
kind: 'literal'
|
|
value: Value
|
|
}
|
|
|
|
export type Variable = {
|
|
kind: 'variable'
|
|
name: string
|
|
}
|
|
|
|
export type Constructor = {
|
|
kind: 'constructor'
|
|
name: string
|
|
}
|
|
|
|
// Functions
|
|
|
|
export type Lambda = {
|
|
kind: 'lambda'
|
|
params: string[]
|
|
body: AST
|
|
}
|
|
|
|
export type Apply = {
|
|
kind: 'apply'
|
|
func: AST
|
|
args: AST[]
|
|
}
|
|
|
|
// Bindings
|
|
|
|
export type Let = {
|
|
kind: 'let'
|
|
name: string
|
|
value: AST
|
|
body: AST
|
|
}
|
|
|
|
// Matching
|
|
|
|
export type Match = {
|
|
kind: 'match'
|
|
expr: AST
|
|
cases: MatchCase[]
|
|
}
|
|
|
|
export type MatchCase = {
|
|
pattern: Pattern
|
|
result: AST
|
|
}
|
|
|
|
export type Pattern =
|
|
| { kind: 'wildcard' }
|
|
| { kind: 'var', name: string }
|
|
| { kind: 'literal', value: number | string }
|
|
| { kind: 'constructor', name: string, args: Pattern[] }
|
|
| { kind: 'list', elements: Pattern[] }
|
|
| { kind: 'record', fields: { [key: string]: Pattern } }
|
|
|
|
// Data Structures
|
|
|
|
export type Record = {
|
|
kind: 'record'
|
|
fields: { [key: string]: AST }
|
|
}
|
|
|
|
export type RecordAccess = {
|
|
kind: 'record-access'
|
|
record: AST
|
|
field: string
|
|
}
|
|
|
|
export type RecordUpdate = {
|
|
kind: 'record-update'
|
|
record: AST
|
|
updates: { [key: string]: AST }
|
|
}
|
|
|
|
export type List = {
|
|
kind: 'list'
|
|
elements: AST[]
|
|
}
|
|
|
|
// Top-level constructs
|
|
|
|
export type Definition = {
|
|
kind: 'definition'
|
|
name: string
|
|
body: AST
|
|
// type?: string // TODO
|
|
}
|
|
|
|
export type TypeDef = {
|
|
kind: 'typedef'
|
|
name: string
|
|
variants: Array<{ name: string, args: string[] }>
|
|
}
|
|
|
|
export type Import = {
|
|
kind: 'import'
|
|
module: string
|
|
items: string[] | 'all'
|
|
}
|
|
|
|
export type AST =
|
|
| Literal
|
|
| Variable
|
|
| Constructor
|
|
| Lambda
|
|
| Apply
|
|
| Let
|
|
| Match
|
|
| Record
|
|
| RecordAccess
|
|
| RecordUpdate
|
|
| List
|
|
| Definition
|
|
| TypeDef
|
|
| Import
|
|
|
|
export function prettyPrint(ast: AST, indent = 0): string {
|
|
const i = ' '.repeat(indent);
|
|
|
|
switch (ast.kind) {
|
|
case 'literal':
|
|
return `${i}${ast.value.value}`;
|
|
|
|
case 'variable':
|
|
return `${i}${ast.name}`;
|
|
|
|
case 'constructor':
|
|
return `${i}${ast.name}`;
|
|
|
|
case 'apply':
|
|
const func = prettyPrint(ast.func, 0);
|
|
const args = ast.args.map(a => prettyPrint(a, 0)).join(' ');
|
|
return `${i}(${func} ${args})`
|
|
|
|
case 'let':
|
|
return `${i}let ${ast.name} = \n${prettyPrint(ast.value, indent + 1)}\n${i}in\n${prettyPrint(ast.body, indent + 1)}`
|
|
|
|
case 'list':
|
|
const elems = ast.elements.map(e => prettyPrint(e, 0)).join(', ');
|
|
return `${i}[${elems}]`;
|
|
|
|
case 'record':
|
|
const fields = Object.entries(ast.fields)
|
|
.map(([k, v]) => `${k} = ${prettyPrint(v, 0)}`)
|
|
.join(', ');
|
|
return `${i}{${fields}}`;
|
|
|
|
case 'lambda':
|
|
const params = ast.params.join(', ')
|
|
return `${i}(${params}) => ${prettyPrint(ast.body)}`
|
|
|
|
case 'record-access':
|
|
return `${i}${prettyPrint(ast.record)}.${ast.field}`
|
|
|
|
case 'record-update':
|
|
const updates = Object.entries(ast.updates).map(([k, v]) => `${k} = ${prettyPrint(v, 0)}`).join(', ');
|
|
return `${i}${prettyPrint(ast.record)} { ${updates} }`
|
|
|
|
default:
|
|
return `${i}${ast.kind}`
|
|
}
|
|
}
|