]>
git.kianting.info Git - uann/blob - index.ts
e22fdbbdd6550fe81c5779546472dd388927d95a
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 * @param matched : string have been matched
31 * @param remained : string will be tested whether it'll be matched.
33 export type MatcheePair
= {matched
: string; remained
: string};
37 * it returns a function which test if the first char of the `remained` part of
38 * the argument of the function is `c`, if it's true, update the `MatchedPair` wrapped
39 * in `Some`. Otherwise, it returns `None`.
40 * * @param c : the char to be test.
41 * @returns the updated `MatchedPair` wrapped in `Some(x)` or `None`.
43 export function match1Char(c
: string) : (m
: MatcheePair
) => Maybe
<MatcheePair
> {
44 return (m
: MatcheePair
)=>{
45 if (m
.remained
.length
== 0){
46 return { _tag
: "None" };
48 const charToBeMatched
= m
.remained
[0];
49 if (charToBeMatched
=== c
){
50 return {_tag
: "Some", value
:{
51 matched
: m
.matched
+ charToBeMatched
,
52 remained
: m
.remained
.substring(1)}};
55 return {_tag
: "None"};
62 * @param m : the `MatcheePair` to be consumed.
63 * @returns if the length of `m.remained` >= 1; consumes the matchee by 1 char and wraps it in `Some`,
64 * otherwise, returns `None`.
66 export function matchAny(m
: MatcheePair
) : Maybe
<MatcheePair
>{
67 if (m
.remained
.length
>= 1){
68 return {_tag
: "Some", value
:{
69 matched
: m
.matched
+ m
.remained
[0],
70 remained
: m
.remained
.substring(1)}};
72 return {_tag
: "None"};
78 * it returns a function which test if the first char of the `remained` part of
79 * the argument of the function is between `l` and `u`, if it's true, update the `MatchedPair` wrapped
80 * in `Some`. Otherwise, it returns `None`.
81 * * @param l : lower bound char, 1-char string
82 * * @param u : upper bound char, 1-char string
83 * @returns the updated `MatchedPair` wrapped in `Some(x)` or `None`.
85 export function matchRange(l
: string, u
: string) : (m
: MatcheePair
) => Maybe
<MatcheePair
> {
86 let lCodepoint
= charToCodepoint(l
);
87 let uCodepoint
= charToCodepoint(u
);
89 throw new Error("Error: the codepoint of `"+l
+"` is not smaller than `"+u
+"`)");
91 return (m
: MatcheePair
)=>{
92 if (m
.remained
.length
< 1){
93 return {_tag
: "None"};
95 const charToBeMatched
= m
.remained
[0];
96 const codePointToBeMatched
= charToCodepoint(charToBeMatched
);
97 if (codePointToBeMatched
>= lCodepoint
&& codePointToBeMatched
<= uCodepoint
){
98 return {_tag
: "Some", value
:{
99 matched
: m
.matched
+ charToBeMatched
,
100 remained
: m
.remained
.substring(1)}};
103 return {_tag
: "None"};
109 * convert the one-char string to codepoint.
110 * @param s : the string to code point.
111 * @returns if `s.length > 1` return error; otherwise, return the codepoint of `s`.
113 export function charToCodepoint(s
: string): number{
115 throw new Error("Error: the length of input string for "+s
+ "is "+s
.length
+`,
116 however, it should be 1.`);
118 return s
.charCodeAt(0);
123 * @description thendo(input, f, ...) like
125 * @param input: the wrapped input.
126 * @param f: the function to be applied.
128 * @returns:the applied wrapped result `MatcheePair`.
130 export function thenDo
<T
>(input
: Maybe
<T
>, f
: Function) : Maybe
<T
>{
131 if (input
._tag
== "None"){
135 let inner
= input
.value
;
141 * @description "or", like the regex `( f1 | f2 )` .
142 * It returns a function `f` of which the argument is`x`.
143 * if `f1(x)` is None, then `f` returns `f2(x)`. Otherwise,
144 * `F` returns `f1(x)`.
145 * @param f1 : 1st function to be compared
146 * @param f2 : 2nd function to be compared
147 * @returns:the combined function
149 export function orDo
<T
>(f1
: Function, f2
: Function) : (x
: T
) => Maybe
<T
>{
151 let f1x
: Maybe
<T
> = (f1(x
));
153 if (f1x
._tag
== "None"){
165 * @description repeating matching function `f`
166 * zero or more times, like the asterisk `*` in regex `f*` .
167 * @param f : the function to be repeated 0+ times.
168 * @returns:the combined function
170 export function zeroOrMoreDo
<T
>(f
: Function): (x
: T
) => Maybe
<T
>{
172 var wrapped_old_x
: Maybe
<T
> = {_tag
: "Some", value
: x
};
173 var wrapped_new_x
: Maybe
<T
> = wrapped_old_x
;
175 while (wrapped_new_x
._tag
!= "None"){
176 wrapped_old_x
= wrapped_new_x
;
177 wrapped_new_x
= thenDo(wrapped_old_x
, f
);
180 return wrapped_old_x
;
185 * @description Not. like the `^` inside regex of [^f].
186 * returns a function `F(x)` such that if `f(x)` is `None`,
187 * returns the x consuming a char; if `f(x)` is not None, F(x)
189 * @param f: the function forbidden to be matched.
190 * @returns: combined function `F`.
192 export function notDo
<T
>(f
: Function): (x
: T
) => Maybe
<T
>{
194 let wrapped_x
: Maybe
<T
> = {
198 let f_x
= thenDo(wrapped_x
, f
);
200 if (f_x
._tag
!= "None"){
201 return {_tag
:"None"};
203 return thenDo(wrapped_x
, matchAny
);
209 * if `x` is matched by `f` once, returns `f(x)`. Otherwise,
211 * similar to `?` in regex `f?`.
212 * @param f : the function to be matched
213 * @returns return wrapped f(x)
215 export function zeroOrOnceDo
<T
>(f
: Function): (x
: T
) => Maybe
<T
>{
217 var wrapped_old_x
: Maybe
<T
> = {_tag
: "Some", value
: x
};
218 var wrapped_new_x
= thenDo(wrapped_old_x
, f
);
220 if (wrapped_new_x
._tag
!= "None"){
221 return wrapped_new_x
;
223 return wrapped_old_x
;
229 export function tokenize(input
: string){
230 var input_matchee_pair
: Maybe
<MatcheePair
> = toSome(
234 // integer = ([+]|[-])\d\d?
235 let integer
= (x
: MatcheePair
) =>
236 { let wrapped_x
= toSome(x
);
237 let plusMinus
= orDo(match1Char('+'), match1Char('-')); // ([+]|[-])
238 let d
= matchRange('0','9'); // \d
239 return thenDo(thenDo(thenDo(wrapped_x
,
240 zeroOrOnceDo(plusMinus
)),d
),
243 console
.log(input
+", result: ");
244 console
.log(thenDo(input_matchee_pair
, integer
));
245 // TODO: id, string, space, basic operator, 3 marks: @, {, }.