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

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}`
}
}