]>
git.kianting.info Git - clo/blob - src/parser.ts
2 * parser.ts - parser and js generator of clo.
4 import * as p from
'typescript-parsec';
5 import { Token
} from
'typescript-parsec';
13 * convert a `tkTree` AST to S-expr string
14 * @param t the `tkTree`
15 * @returns S-expr String
17 export function tkTreeToSExp(t: tkTree): string{
20 if (Array.isArray(t)){
21 let strArray = t.map((x)=>tkTreeToSExp(x));
22 str = "(" + strArray.join("◎") + ")";
34 export type tkTree
= string | tkTree
[];
36 export enum TokenKind
{
53 export const lexer
= p
.buildLexer([
54 [true, /^\d
+(\
.\d
+)?/g
, TokenKind
.Number],
55 [true, /^[\\][\\]/g
, TokenKind
.Op
],
56 [true, /^\\\@
/g
, TokenKind
.ExcapeAt
],
57 [true, /^\
/\
*([^/]|\
/[^*])*\
*\
//g, TokenKind.Comment],
58 [true, /^\
;/g
, TokenKind
.Semicolon
],
59 [true, /^[-][-][-]/g
, TokenKind
.Seperator
],
60 [true, /^[\
+\
-\
*\
/\
&\
|\
!\
^\
<\
>\
~\
=\?]+/g
, TokenKind
.Op
],
61 [true, /^\@
/g
, TokenKind
.ExprMark
],
62 [true, /^[()\
[\
]{}]/g
, TokenKind
.Paren
],
63 [true, /^[\"]([^\"]|[\\].)*[\"]/g
, TokenKind
.Str
],
64 [true, /^[\']([^\']|[\\].)*[\']/g
, TokenKind
.Str
],
65 [true, /^[()\
[\
]{}]/g
, TokenKind
.Paren
],
66 [true, /^[^\
/\\\@\s
\n\t\r;]+/g
, TokenKind
.Id
],
67 [true, /^(\s
|\n|\r|\t)+/g
, TokenKind
.SpaceNL
],
78 export const PROG
= p
.rule
<TokenKind
, tkTree
>();
79 export const SEGMENT
= p
.rule
<TokenKind
, tkTree
>();
80 export const IMPORT
= p
.rule
<TokenKind
, tkTree
>();
81 export const IMPORTS
= p
.rule
<TokenKind
, tkTree
>();
82 export const SEMICOLON
= p
.rule
<TokenKind
, tkTree
>();
83 export const NOT_AT_TEXT
= p
.rule
<TokenKind
, tkTree
>();
84 export const CONTENT
= p
.rule
<TokenKind
, tkTree
>();
87 export function applySegment(input
: [Token
<TokenKind
>, Token
<TokenKind
>[],
88 Token
<TokenKind
>]): tkTree
[]{
89 let unpackedInnerExprs
= input
[1].map((x
)=>{return x
.text
});
90 return ["%exprs", unpackedInnerExprs
];
93 export function applySemiColon(value
: Token
<TokenKind
.Semicolon
>): tkTree
{
97 export function applyParts(first
: tkTree
,
98 second
: [Token
<TokenKind
>, Token
<TokenKind
>, tkTree
]):tkTree
{
99 return ["%clo", first
, second
[2]];
102 export function applyPartsWithoutImport(
103 parsed
: [Token
<TokenKind
>, Token
<TokenKind
>, tkTree
]):tkTree
{
104 return ["%clo", "" , parsed
[2]];
108 export function applyComment(value
: Token
<TokenKind
.Comment
>): tkTree
[]{
113 export function applyImport(input
: [Token
<TokenKind
>,Token
<TokenKind
>[], tkTree
]) : tkTree
{
114 let importTail
= input
[1].map(x
=>x
.text
);
115 return ["import"].concat(importTail
);
120 function applyImportComment(input: [Token<TokenKind>,Token<TokenKind>[],
121 tkTree, Token<TokenKind.Comment>]) : tkTree{
122 let importTail = input[1].map(x=>x.text);
123 let comment = [input[3].text];
124 return ["import"].concat(importTail).concat(comment);
127 export function applyImports(input
: [tkTree
, tkTree
[]]): tkTree
{
128 let resultBody
= [input
[0]].concat(input
[1]);
129 let resultWrapper
= ["%import", resultBody
];
130 return resultWrapper
;
136 export function applyNotAtText(value
: Token
<TokenKind
>): tkTree
{
137 if (value
.text
== "\\\@"){
140 else{return value
.text
;}
143 export function applyText (input
: tkTree
): tkTree
[]{
144 return ["%text", input
];
147 export function applyContent(input
: tkTree
[]): tkTree
[]{
148 return ["%content", input
];
151 export function applySpaceNL(value
: Token
<TokenKind
.SpaceNL
>): tkTree
{
156 * IMPORTEE: Number, Op, Paren, Id, Str, Comment,
158 export let IMPORTEE
= p
.alt(p
.tok(TokenKind
.Number),
160 p
.tok(TokenKind
.Paren
),
162 p
.tok(TokenKind
.Str
),
163 p
.tok(TokenKind
.SpaceNL
),
164 p
.tok(TokenKind
.Comment
));
166 export let NOT_AT
= p
.alt(p
.tok(TokenKind
.Seperator
),
167 p
.tok(TokenKind
.Semicolon
),
168 p
.tok(TokenKind
.Number),
169 p
.tok(TokenKind
.ExcapeAt
),
171 p
.tok(TokenKind
.Paren
),
172 p
.tok(TokenKind
.SpaceNL
),
174 p
.tok(TokenKind
.Str
),
175 p
.tok(TokenKind
.Comment
),
179 * PROG : IMPORTS '---' NEWLINE CONTENT | '---' NEWLINE CONTNENT
183 p
.lrec_sc(IMPORTS
, p
.seq(p
.str('---'), p
.str("\n"), CONTENT
), applyParts
),
184 p
.apply(p
.seq(p
.str('---'), p
.str("\n"), CONTENT
), applyPartsWithoutImport
))
189 * NOT_AT_TEXT : NOT_AT
191 NOT_AT_TEXT
.setPattern(
192 p
.apply(NOT_AT
, applyNotAtText
)
196 p
.apply( p
.seq(IMPORT
, p
.rep(IMPORT
)), applyImports
)
201 * 'import' IMPORTEE* SEMICOLON |
206 p
.apply(p
.seq(p
.str('import'), p
.rep_sc(IMPORTEE
), SEMICOLON
),
208 p
.apply(p
.tok(TokenKind
.Comment
), applyComment
),
209 p
.apply(p
.tok(TokenKind
.SpaceNL
), applySpaceNL
)
217 SEMICOLON
.setPattern(
218 p
.apply(p
.tok(TokenKind
.Semicolon
), applySemiColon
)
224 * SEGMENT : '@' NOT_AT* '@' |
225 * (NOT_AT_TEXT | EXCAPE_AT)*
229 p
.apply(p
.rep_sc(NOT_AT_TEXT
), applyText
),
230 p
.apply(p
.seq(p
.str('@'), p
.rep(NOT_AT
), p
.str('@')), applySegment
),
238 p
.apply(p
.rep(SEGMENT
), applyContent
)
244 * the head part of the output JS code : before import
246 export let outputHead
= `
247 /* clo, a typesetting engine, generated JS file*/
248 /* CLO: beginning of head*/
250 let cloLib = require("./src/libclo/index.js");
251 let clo = new cloLib.Clo();
253 /* CLO: end of head*/\n`
256 * the middle part of the output JS code : between import part and content part
258 export let outputMiddle
=`
259 /* CLO: beginning of middle part*/
260 clo.mainStream = /* CLO: end of middle part*/
264 * the end part of the output JS code : after content part
266 export let outputEnd
=`
267 /* CLO: beginning of end part*/
269 /*CLO : end of end part*/
273 * Convert `tree` (ASTTree; `tkTree`) to JS Code.
275 export function treeToJS(tree
: tkTree
): string{
279 let totalResult
= outputHead
+ treeToJS(tree
[1]) +
280 outputMiddle
+ treeToJS(tree
[2]) + outputEnd
;
283 if (head
== "%import"){
284 let imports
= tree
[1];
285 if (Array.isArray(imports
)){
286 let importsText
= imports
.map(
288 if (Array.isArray(x
)){
289 return x
.join('') + ';';
295 let importTextCombined
= importsText
.join('');
296 return importTextCombined
;
302 if (head
== "%content"){
304 if (Array.isArray(tail
)){
305 if (tail
.length
== 1){
306 return tail
.map((x
)=>treeToJS(x
)).join("').concat('")+ ";";
308 let tailStrings
= tail
.map((x
)=>treeToJS(x
));
309 return "(" + tailStrings
.join(').concat(') + ");";
314 if (head
== "%text"){
315 let textContents
= tree
[1];
316 if (Array.isArray(textContents
)){
317 let decoratedArray
= textContents
318 .flatMap(x
=>String(x
))
319 .map(x
=>x
.replace("\`","\\\`"));
321 return "[`" + decoratedArray
.join("\`, \`") + "`]";
323 let decorated
= textContents
.replace("\`","\\\`");
325 return "[`" + decorated
+ "`]";
329 if (head
== "%exprs"){
330 let content
= tree
[1];
331 if (Array.isArray(content
)){
332 let flattenContent
= content
.flat();
333 return flattenContent
.join('');
341 if (Array.isArray(tree
)){
342 return tree
.join('');
351 * `inputText` to `tkTree` (ASTTree)
353 export function inputTextToTree(inputText
: string){
354 // force convert Windows newline to Linux newline
355 inputText
= inputText
.replace("\r\n", "\n");
357 return p
.expectSingleResult(
358 p
.expectEOF(PROG
.parse(lexer
.parse(inputText
))));