summaryrefslogtreecommitdiff
path: root/src/lib/core/eval.ts
blob: 59cda1d435a1722d4a8a3da725bdbbee9788f234 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { ASTNode, NodeType } from "./common";
import { Lex } from "./lexer";
import { Parse } from "./parser";

export type FnError<T extends string> = `Function execution error: ${T}`;

export type ToStringInner<T, Carry extends string = ""> = T extends
  | string
  | number
  ? `${T}`
  : T extends readonly any[]
  ? T extends readonly [infer Head, ...infer Tail]
    ? `${ToStringInner<
        Tail,
        `${Carry extends "" ? "" : `${Carry}, `}${ToStringInner<Head>}`
      >}`
    : `[${Carry}]`
  : FnError<`Can't stringify`>;

export type UnarrayIfOnlyHead<T extends readonly any[]> = T extends [
  infer Head,
  infer Next
]
  ? T
  : T extends [infer Head]
  ? Head
  : T;

export type NumberToArray<
  Number extends number,
  Carry extends readonly any[] = []
> = Number extends Carry["length"]
  ? Carry
  : NumberToArray<Number, [...Carry, any]>;

export type NumbersToArray<
  Numbers extends readonly number[],
  Carry extends readonly any[] = []
> = Numbers extends [
  infer Head extends number,
  ...infer Tail extends readonly number[]
]
  ? NumbersToArray<Tail, [...Carry, ...NumberToArray<Head>]>
  : Carry;

export type AddNumbers<Numbers extends readonly number[]> =
  NumbersToArray<Numbers> extends infer T extends readonly any[]
    ? T["length"]
    : never;

export type AddStrings<
  Strings extends readonly string[],
  Carry extends string = ""
> = Strings extends [infer Head extends string, ...infer Tail extends string[]]
  ? AddStrings<Tail, `${Carry}${Head}`>
  : Carry;

export type MultiplyInner<
  N extends number,
  MS extends readonly any[],
  Carry extends number = 0
> = MS extends [infer Head extends number, ...infer Tail extends readonly any[]]
  ? MultiplyInner<N, Tail, AddNumbers<[Carry, N]>>
  : Carry;
export type Multiply<M extends number, N extends number> = MultiplyInner<
  M,
  NumberToArray<N>
>;

export type BUILTIN_Arr<Args extends readonly any[]> = Args;

export type BUILTIN_ToString<Args extends readonly any[]> = ToStringInner<
  UnarrayIfOnlyHead<{
    [Idx in keyof Args]: ToStringInner<Args[Idx]>;
  }>
>;

export type BUILTIN_Add<Args extends readonly any[]> =
  Args extends readonly string[]
    ? AddStrings<Args>
    : Args extends readonly number[]
    ? AddNumbers<Args>
    : FnError<`Cannot add operands ${ToStringInner<Args>}`>;

export type BUILTIN_Mul<Args extends readonly any[]> = Args extends [
  infer A,
  infer B,
  infer C
]
  ? FnError<`Can only multiply [number, number], but got ${ToStringInner<Args>}`>
  : Args extends [infer M extends number, infer N extends number]
  ? Multiply<M, N>
  : FnError<`Can only multiply [number, number], but got ${ToStringInner<Args>}`>;

export type SENTINEL_NO_BUILTIN = "__NO_BUILTIN__";
export type MapBuiltins<Node extends ASTNode> =
  Node["children"] extends infer Children extends readonly ASTNode[]
    ? {
        [Idx in keyof Children]: Children[Idx] extends ASTNode
          ? Evaluate<Children[Idx]>
          : never;
      } extends infer Args extends readonly any[]
      ? Node["name"] extends "tostring"
        ? BUILTIN_ToString<Args>
        : Node["name"] extends "arr"
        ? BUILTIN_Arr<Args>
        : Node["name"] extends "add"
        ? BUILTIN_Add<Args>
        : Node["name"] extends "mul"
        ? BUILTIN_Mul<Args>
        : SENTINEL_NO_BUILTIN
      : never
    : never;

export type EvalError<T extends string> = `Eval error: ${T}`;

export type Evaluate<Node extends ASTNode> = Node["type"] extends NodeType.INT
  ? Node["value"]
  : Node["type"] extends NodeType.EXT
  ? MapBuiltins<Node>
  : EvalError<`Unhandled node type ${Node["type"]}`>;

const input = `` as const;
const lex_result = null as unknown as Lex<typeof input>;
const parse_result = null as unknown as Parse<typeof lex_result>;
const eval_result = null as unknown as Evaluate<typeof parse_result>;