@esolangs/typina
Version:
Pineapple interpreter implemented in TypeScript's type system
100 lines (90 loc) • 3.75 kB
text/typescript
/**
* pina.ts - Interpreter of Pineapple, a toy programming language.
*
* @author CismonX <admin@cismon.net>
* @license MIT
*/
/**
* First character of variable name.
*/
type LeadingNameChar = '_'
| 'A' | 'B' | 'C' | 'D' | 'E' | 'F' | 'G' | 'H' | 'I' | 'J' | 'K' | 'L' | 'M'
| 'N' | 'O' | 'P' | 'Q' | 'R' | 'S' | 'T' | 'U' | 'V' | 'W' | 'X' | 'Y' | 'Z'
| 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm'
| 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z';
/**
* Other characters of variable name.
*/
type NameChar = LeadingNameChar | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0';
/**
* Whitespace characters.
*/
type WhiteSpaceChar = ' ' | '\t' | '\r' | '\n';
/**
* Parses the source code of a Pineapple program.
*
* Returns the AST of the program. Or `never` when parse fails.
*/
export type Parse<Code, AST = []> =
TrimLeadingWhiteSpace<Code> extends `print(${infer N})${infer R}` ? ParseName<N, ['next', R, AST]>
: Code extends `${infer Name}=${infer Value}` ? ParseName<Name, ['value', Value, AST]>
: Code extends '' ? AST : never;
/**
* Parse a variable name.
*/
type ParseName<Code extends string, Cont, Value extends string = ''> =
Code extends `${infer C}${infer R}` ?
C extends WhiteSpaceChar ?
Value extends '' ? ParseName<R, Cont>
: TrimLeadingWhiteSpace<R> extends '' ? ParseName<'', Cont, Value> : never
: C extends '$' ? Value extends '' ? ParseName<R, Cont, '$'> : never
: C extends LeadingNameChar ? ParseName<R, Cont, `${Value}${C}`>
: C extends NameChar ? Value extends '$' ? never : ParseName<R, Cont, `${Value}${C}`>
: never
: Value extends `$${infer Name}` ?
Name extends '' ? never
// Finished parsing assignment lvalue. Parse rvalue.
: Cont extends ['value', infer V, infer AST] ? ParseStringLiteral<V, ['next', Name, AST]>
// Finished parsing "print" line. Start parsing next line.
: Cont extends ['next', infer V, infer AST] ?
AST extends [...infer O] ? Parse<V, [...O, ['print', Name]]> : never
: never
: never;
/**
* Parse a string literal (with possible leading or trailing whitespace).
*/
type ParseStringLiteral<Code, Cont> =
Code extends `${infer L}"${infer S}"${infer T}` ?
TrimLeadingWhiteSpace<L> extends `${infer _}${infer _}` ? never
// Finished parsing assignment rvalue. Start parsing next line.
: Cont extends ['next', infer V, infer AST] ?
AST extends [...infer O] ? Parse<T, [...O, ['assign', V, S]]> : never
: never
: never;
/**
* Returns the given string with leading whitespace removed.
*/
type TrimLeadingWhiteSpace<Code> =
Code extends `${infer C}${infer R}` ? C extends WhiteSpaceChar ? TrimLeadingWhiteSpace<R> : Code : '';
/**
* Evaluates the AST of a Pineapple program.
*
* Returns the output of program execution. Or `never` when execution fails.
*/
export type Eval<AST, VarMap = [], Output extends string = ''> =
AST extends [infer L, ...infer R] ?
VarMap extends [...infer V] ?
// Prepend entry to variable map, so that the reassigned value will be fetched first.
L extends ['assign', infer Name, infer Value] ? Eval<[...R], [[Name, Value], ...V], Output>
: L extends ['print', infer Name] ? Eval<[...R], VarMap, `${Output}${VarValue<VarMap, Name>}`>
: never
: never
: Output;
/**
* Get variable value by name from a name-value map of variables.
*/
type VarValue<VarMap, Name> =
VarMap extends [infer L, ...infer R] ?
L extends [infer N, infer V] ? N extends Name ? V : VarValue<[...R], Name>
: never
: never;