UNPKG

@esolangs/typina

Version:

Pineapple interpreter implemented in TypeScript's type system

100 lines (90 loc) 3.75 kB
/** * 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;