]> git.kianting.info Git - clo/blob - src/index.ts
add some lexical rules
[clo] / src / index.ts
1 var fs = require('fs');
2 import { argv, resourceUsage } from 'node:process';
3 import * as tk from './tokenize.js';
4 import * as util from 'util';
5 import { drawEllipsePath, reduceRotation } from 'pdf-lib';
6 import { isTypedArray } from 'node:util/types';
7 import { error } from 'node:console';
8
9 /**
10 * debug reprensenting
11 */
12 let repr = (x : any)=>{return util.inspect(x, {depth: null})};
13
14 /**
15 * token tree type.
16 */
17 type tkTree = tkTree[] | tk.Token
18
19 /**
20 * concated 2 `tkTree`s
21 * @param x the array to be concated
22 * @param y the item or array to ve concated
23 * @returns concated tkTree array, or thrown error if can't be concated.
24 */
25 function concat(x: tkTree, y:tkTree): tkTree[] {
26 if (Array.isArray(x)){
27 return x.concat(y);
28 }else{
29 throw new Error("the tkTree can't be concated, because it's not an array.");
30
31 }
32 }
33
34 function slice(x: tkTree, index?:number, end?:number): tkTree[] {
35 if (Array.isArray(x)){
36 return x.slice(index,end);
37 }else{
38 throw new Error("the tkTree can't be concated, because it's not an array.");
39
40 }
41 }
42
43 /**
44 * TokenMatcheePair for tokens' parser combinator
45 * matched: the matched (now and before) tokens
46 * remained: tokens to be matched
47 * ast: abstract syntax tree
48 */
49 export interface TokenMatcheePair {
50 matched: tk.Token[]
51 remained: tk.Token[]
52 ast : tkTree[]
53 }
54
55 /**
56 * @description
57 * match one token type.
58 *
59 * it returns a function which test if the type of first token of the `remained` part of
60 * the argument of the function is `typ` , if it's true, update the `TokenMatcheePair` wrapped
61 * in `Some`. Otherwise, it returns `None`.
62 * * @param typ : the type to be test.
63 * @returns the updated `TokenMatcheePair` wrapped in `Some(x)` or `None`.
64 */
65 export function m1TType(typ: tk.TokenType):
66 (m: TokenMatcheePair) => tk.Maybe<TokenMatcheePair> {
67 return (m: TokenMatcheePair) => {
68 if (m.remained.length == 0) {
69 return { _tag: "None" };
70 }
71 /**
72 * token to be matched
73 * */
74 const ttbm = m.remained[0];
75
76 if (ttbm.type == typ) {
77 let new_matched = m.matched.concat(ttbm);
78 let result : tk.Some<TokenMatcheePair> = {
79 _tag: "Some", value: {
80 matched: new_matched,
81 remained: m.remained.slice(1),
82 ast: ([ttbm]),
83 }
84 };
85 return result;
86 }
87 else {
88 return { _tag: "None" };
89 }
90 }
91 };
92
93 /**
94 * type int
95 */
96 let tInt = m1TType(tk.TokenType.INT);
97 let tAdd = m1TType(tk.TokenType.I_ADD);
98 let tSub = m1TType(tk.TokenType.I_SUB);
99 let tMul = m1TType(tk.TokenType.I_MUL);
100 let tDiv = m1TType(tk.TokenType.I_DIV);
101 let tLParen = m1TType(tk.TokenType.L_PAREN);
102 let tRParen = m1TType(tk.TokenType.R_PAREN);
103
104
105
106 argv.forEach((val, index) => {
107 console.log(`${index}=${val}`);
108 });
109
110
111 /**
112 * like `m ==> f` in ocaml
113 * @param m matchee wrapped
114 * @param f matching function
115 * @returns wrapped result
116 */
117 function thenDo(m : tk.Maybe<TokenMatcheePair>, f : Function){
118 if (m._tag == "None"){
119 return m;
120 }else{
121 var a : tk.Maybe<TokenMatcheePair> = f(m.value);
122 if (a._tag == "Some"){
123 a.value.ast = concat(m.value.ast, a.value.ast);
124 }
125
126 return a;
127 }
128 }
129
130 /**
131 * like `f1 | f2` in regex
132 * @param f1 the first tried function
133 * @param f2 the second tried function
134 * @returns wrapped result
135 */
136 function orDo(f1 : Function, f2 : Function){
137 return (x : TokenMatcheePair) =>{
138 let res1 : tk.Maybe<TokenMatcheePair> = f1(x);
139 if (res1._tag == "Some"){
140 return res1;
141 }else{
142 let res2 : tk.Maybe<TokenMatcheePair> = f2(x);
143 return res2;
144 }
145 }
146
147 }
148
149 /**
150 * aux function for midfix operator
151 * @param f function
152 * @param signal the rule name
153 * @returns
154 */
155 let midfix = (f : Function, signal? : string) => (x : TokenMatcheePair)=>{
156 var a = f(x);
157 if (a._tag == "Some"){
158 let ast_tail : tkTree[] = slice(a.value.ast,a.value.ast.length-3);
159 let new_ast = [ast_tail];
160 a.value.ast = new_ast;
161
162 console.log("+"+signal+"+"+repr(a));
163
164
165 }
166 return a;
167 }
168
169 let circumfix = (f : Function, signal? : string) => (x : TokenMatcheePair)=>{
170 var a = f(x);
171 if (a._tag == "Some"){
172 let inner = a.value.ast[a.value.ast.length-2];
173 console.log("AST===="+repr(a.value.ast));
174 let ast_middle : tkTree[] = [inner];
175 let new_ast = [ast_middle];
176 a.value.ast = new_ast;
177
178 console.log("+"+signal+"+"+repr(a));
179
180
181 }
182 return a;
183 }
184
185 /** fac1 = "(" expr ")" */
186 let fac1 = circumfix((x : TokenMatcheePair)=>
187 thenDo(thenDo(thenDo(tk.toSome(x), tLParen), expr), tRParen), "fac1");
188
189 let fac2 = tInt;
190
191 let fac = orDo(fac1, fac2);
192
193
194
195 /**
196 *
197 * term1 = fac (MUL | DIV) fac
198 */
199
200 let term1 = midfix((x : TokenMatcheePair)=>
201 thenDo(thenDo(thenDo(tk.toSome(x), fac), orDo(tMul,tDiv)), fac), "term1");
202
203
204 /**
205 *
206 * term2 = int MUL int
207 */
208 let term2 = fac;
209
210 /**
211 * term = term1 | term2
212 */
213 let term = orDo(term1, term2);
214
215
216 /**
217 *
218 * expr1 = term ADD term
219 */
220 let expr1 = midfix((x : TokenMatcheePair)=>
221 thenDo(thenDo(thenDo(tk.toSome(x), term), orDo(tAdd,tSub)), term), "expr1");
222 /**
223 * expr2 = term
224 */
225 let expr2 = term;
226
227 /**
228 * expr = expr1 | expr2
229 */
230 let expr = orDo(expr1, expr2);
231
232
233
234
235 let tokens = tk.tokenize("(4-(3/4))");//tk.tokenize(argv[2]);
236 let tokensFiltered = tokens.filter(
237 (x)=>{return (x.type != tk.TokenType.NL
238 && x.type != tk.TokenType.SP)});
239
240 let wrappedTokens : tk.Maybe<TokenMatcheePair> =
241 tk.toSome({
242 matched : [] ,
243 remained : tokensFiltered,
244 ast : []});
245
246 let beta = expr({
247 matched : [] ,
248 remained : tokensFiltered,
249 ast : []});
250
251
252 console.log(repr(beta));
253