]>
git.kianting.info Git - uann/blob - index.ts
883d94b742145129ff948e0325d94457b274b8f1
1 var fs
= require('fs');
3 export type Some
<T
> = { _tag
: "Some"; value
: T
};
4 export type None
= {_tag
: "None"};
8 * wrap a x in a `Some(T)`
9 * @param x : variable to be wrapped.
10 * @returns wrapped `x`.
12 function toSome
<T
>(x
: T
): Some
<T
>{
13 return { _tag
: "Some", value
: x
};
16 * @description Like the `Some(a)` and `None` in Rust.
20 * let exam1 : Maybe<Number> = { _tag: "Some", value: 12 };
21 * let exam2 : Maybe<Number> = None;
24 export type Maybe
<T
> = Some
<T
> | None
;
29 * the pair of the string to be matched later and the string that have been matched
30 * @var matched : have been matched
31 * @var remained : will be tested whether it'll be matched.
32 * @var matched_type (optional): the type of the matched string
34 export interface MatcheePair
{
37 matched_type
?: TokenType
44 * SP, // half-width space and tab
50 * OP, // operator or something like it
56 export enum TokenType
{
58 SP
, // half-width space and tab
68 * @var text : the content text
69 * @var type (optional): the type of the token
70 * @var col : the column number
71 * @var ln : the line number
73 export interface Token
{
82 * it returns a function which test if the first char of the `remained` part of
83 * the argument of the function is `c`, if it's true, update the `MatchedPair` wrapped
84 * in `Some`. Otherwise, it returns `None`.
85 * * @param c : the char to be test.
86 * @returns the updated `MatchedPair` wrapped in `Some(x)` or `None`.
88 export function match1Char(c
: string) : (m
: MatcheePair
) => Maybe
<MatcheePair
> {
89 return (m
: MatcheePair
)=>{
90 if (m
.remained
.length
== 0){
91 return { _tag
: "None" };
93 const charToBeMatched
= m
.remained
[0];
94 if (charToBeMatched
=== c
){
95 return {_tag
: "Some", value
:{
96 matched
: m
.matched
+ charToBeMatched
,
97 remained
: m
.remained
.substring(1)}};
100 return {_tag
: "None"};
107 * @param m : the `MatcheePair` to be consumed.
108 * @returns if the length of `m.remained` >= 1; consumes the matchee by 1 char and wraps it in `Some`,
109 * otherwise, returns `None`.
111 export function matchAny(m
: MatcheePair
) : Maybe
<MatcheePair
>{
112 if (m
.remained
.length
>= 1){
113 return {_tag
: "Some", value
:{
114 matched
: m
.matched
+ m
.remained
[0],
115 remained
: m
.remained
.substring(1)}};
117 return {_tag
: "None"};
123 * it returns a function which test if the first char of the `remained` part of
124 * the argument of the function is between `l` and `u`, if it's true, update the `MatchedPair` wrapped
125 * in `Some`. Otherwise, it returns `None`.
126 * * @param l : lower bound char, 1-char string
127 * * @param u : upper bound char, 1-char string
128 * @returns the updated `MatchedPair` wrapped in `Some(x)` or `None`.
130 export function matchRange(l
: string, u
: string) : (m
: MatcheePair
) => Maybe
<MatcheePair
> {
131 let lCodepoint
= charToCodepoint(l
);
132 let uCodepoint
= charToCodepoint(u
);
134 throw new Error("Error: the codepoint of `"+l
+"` is not smaller than `"+u
+"`)");
136 return (m
: MatcheePair
)=>{
137 if (m
.remained
.length
< 1){
138 return {_tag
: "None"};
140 const charToBeMatched
= m
.remained
[0];
141 const codePointToBeMatched
= charToCodepoint(charToBeMatched
);
142 if (codePointToBeMatched
>= lCodepoint
&& codePointToBeMatched
<= uCodepoint
){
143 return {_tag
: "Some", value
:{
144 matched
: m
.matched
+ charToBeMatched
,
145 remained
: m
.remained
.substring(1)}};
148 return {_tag
: "None"};
154 * convert the one-char string to codepoint.
155 * @param s : the string to code point.
156 * @returns if `s.length > 1` return error; otherwise, return the codepoint of `s`.
158 export function charToCodepoint(s
: string): number{
160 throw new Error("Error: the length of input string for "+s
+ "is "+s
.length
+`,
161 however, it should be 1.`);
163 return s
.charCodeAt(0);
168 * @description thendo(input, f, ...) like
170 * @param input: the wrapped input.
171 * @param f: the function to be applied.
173 * @returns:the applied wrapped result `MatcheePair`.
175 export function thenDo
<T
>(input
: Maybe
<T
>, f
: Function) : Maybe
<T
>{
176 if (input
._tag
== "None"){
180 let inner
= input
.value
;
186 * @description "or", like the regex `( f1 | f2 )` .
187 * It returns a function `f` of which the argument is`x`.
188 * if `f1(x)` is None, then `f` returns `f2(x)`. Otherwise,
189 * `F` returns `f1(x)`.
190 * @param f1 : 1st function to be compared
191 * @param f2 : 2nd function to be compared
192 * @returns:the combined function
194 export function orDo
<T
>(f1
: Function, f2
: Function) : (x
: T
) => Maybe
<T
>{
196 let f1x
: Maybe
<T
> = (f1(x
));
198 if (f1x
._tag
== "None"){
210 * @description repeating matching function `f`
211 * zero or more times, like the asterisk `*` in regex `f*` .
212 * @param f : the function to be repeated 0+ times.
213 * @returns:the combined function
215 export function zeroOrMoreDo
<T
>(f
: Function): (x
: T
) => Maybe
<T
>{
217 var wrapped_old_x
: Maybe
<T
> = {_tag
: "Some", value
: x
};
218 var wrapped_new_x
: Maybe
<T
> = wrapped_old_x
;
220 while (wrapped_new_x
._tag
!= "None"){
221 wrapped_old_x
= wrapped_new_x
;
222 wrapped_new_x
= thenDo(wrapped_old_x
, f
);
225 return wrapped_old_x
;
230 * @description Not. like the `^` inside regex of [^f].
231 * returns a function `F(x)` such that if `f(x)` is `None`,
232 * returns the x consuming a char; if `f(x)` is not None, F(x)
234 * @param f: the function forbidden to be matched.
235 * @returns: combined function `F`.
237 export function notDo
<T
>(f
: Function): (x
: T
) => Maybe
<T
>{
239 let wrapped_x
: Maybe
<T
> = {
243 let f_x
= thenDo(wrapped_x
, f
);
245 if (f_x
._tag
!= "None"){
246 return {_tag
:"None"};
248 return thenDo(wrapped_x
, matchAny
);
254 * if `x` is matched by `f` once, returns `f(x)`. Otherwise,
256 * similar to `?` in regex `f?`.
257 * @param f : the function to be matched
258 * @returns return wrapped f(x)
260 export function zeroOrOnceDo
<T
>(f
: Function): (x
: T
) => Maybe
<T
>{
262 var wrapped_old_x
: Maybe
<T
> = {_tag
: "Some", value
: x
};
263 var wrapped_new_x
= thenDo(wrapped_old_x
, f
);
265 if (wrapped_new_x
._tag
!= "None"){
266 return wrapped_new_x
;
268 return wrapped_old_x
;
274 export function tokenize(input
: string){
275 var input_matchee_pair
: Maybe
<MatcheePair
> = toSome(
279 // integer = ([+]|[-])?\d\d*
280 let integer
= (x
: MatcheePair
) =>
281 { let wrapped_x
= toSome(x
);
282 let plusMinus
= orDo(match1Char('+'), match1Char('-')); // ([+]|[-])
283 let d
= matchRange('0','9'); // \d
284 var result
= thenDo(thenDo(thenDo(wrapped_x
,
285 zeroOrOnceDo(plusMinus
)),d
),
288 if (result
._tag
== "Some"){
289 result
.value
.matched_type
= TokenType
.INT
;
293 let space
= (x
: MatcheePair
) =>{
294 let wrapped_x
= toSome(x
);
295 let s_aux
= orDo(match1Char(' '), match1Char('\t')); // (" " | "\t")
296 var result
= thenDo(thenDo(wrapped_x
, s_aux
), zeroOrMoreDo(s_aux
));
297 if (result
._tag
== "Some"){
298 result
.value
.matched_type
= TokenType
.SP
;
302 let newline
= (x
: MatcheePair
) =>{
303 let wrapped_x
= toSome(x
);
305 let result
= thenDo(thenDo(wrapped_x
,
306 zeroOrOnceDo(match1Char('\r'))), match1Char('\n'));
307 if (result
._tag
== "Some"){
308 result
.value
.matched_type
= TokenType
.NL
;
313 let term
= (token_list
: Array<Token
>, x
: Some
<MatcheePair
>)=>{
317 let term_list
= [newline
, space
, integer
];
318 let term_aux
= term_list
.reduce((x
,y
)=> orDo(x
,y
));
320 var new_x
: Maybe
<MatcheePair
> = thenDo(old_x
, term_aux
);
321 while (new_x
._tag
!= "None"){
322 if (new_x
.value
.matched_type
!= TokenType
.NL
){
323 col
+= new_x
.value
.matched
.length
;
324 token_list
.push({text
: new_x
.value
.matched
,
325 type: new_x
.value
.matched_type
,
334 token_list
.push({text
: new_x
.value
.matched
,
335 type: new_x
.value
.matched_type
,
342 old_x
= toSome({matched
: "",
343 remained
: new_x
.value
.remained
});
344 new_x
= thenDo(old_x
, term_aux
);
347 if (old_x
.value
.remained
.length
){
348 console
.log(token_list
);
349 throw new Error("the code can't be tokenized is near Ln. "+ln
+", Col."+col
350 +", starting with "+ old_x
.value
.remained
.substring(0,10));
356 console
.log(term([], input_matchee_pair
));
358 // TODO: id, string, space, basic operator, 3 marks: @, {, }.