|
@@ -0,0 +1,173 @@
|
|
|
+import * as moo from "moo";
|
|
|
+import { combineRegex } from "./helper";
|
|
|
+
|
|
|
+export interface I18N_LEXER_TYPES {
|
|
|
+ RK_INTEGER: string;
|
|
|
+ RK_REAL: string;
|
|
|
+ RK_VOID: string;
|
|
|
+ RK_CHARACTER: string;
|
|
|
+ RK_STRING: string;
|
|
|
+ RK_BOOLEAN: string;
|
|
|
+}
|
|
|
+
|
|
|
+export interface I18N_LEXER_BOOLVAL {
|
|
|
+ RK_FALSE: string;
|
|
|
+ RK_TRUE: string;
|
|
|
+}
|
|
|
+
|
|
|
+export interface I18N_LEXER_LOGICOP {
|
|
|
+ RK_LOGICAL_NOT: string;
|
|
|
+ RK_LOGICAL_AND: string;
|
|
|
+ RK_LOGICAL_OR: string;
|
|
|
+}
|
|
|
+
|
|
|
+export interface I18N_LEXER_COMMANDS {
|
|
|
+ RK_PROGRAM: string;
|
|
|
+ RK_SWITCH: string;
|
|
|
+ RK_CASE: string;
|
|
|
+ RK_DEFAULT: string;
|
|
|
+ RK_CONST: string;
|
|
|
+ RK_FUNCTION: string;
|
|
|
+ RK_RETURN: string;
|
|
|
+ RK_FOR: string;
|
|
|
+ RK_FOR_ALT: string;
|
|
|
+ RK_FOR_FROM: string;
|
|
|
+ RK_FOR_TO: string;
|
|
|
+ RK_FOR_PASS: string;
|
|
|
+ RK_BREAK: string;
|
|
|
+ RK_DO: string;
|
|
|
+ RK_DO_UNTIL: string;
|
|
|
+ RK_WHILE: string;
|
|
|
+ RK_WHILE_ALT: string;
|
|
|
+ RK_IF: string;
|
|
|
+ RK_ELSE: string;
|
|
|
+ RK_REFERENCE: string;
|
|
|
+}
|
|
|
+
|
|
|
+export interface I18N_LANG_LIBS {
|
|
|
+ $ioLib: string;
|
|
|
+ $strLib: string;
|
|
|
+ $mathLib: string;
|
|
|
+ $langLib: string;
|
|
|
+ $arrayLib: string;
|
|
|
+}
|
|
|
+
|
|
|
+export interface I18N_LANG_FUNCS {
|
|
|
+ main_function: string;
|
|
|
+ $read: string;
|
|
|
+ $write: string;
|
|
|
+ $numElements: string;
|
|
|
+ $matrixLines: string;
|
|
|
+ $matrixColumns: string;
|
|
|
+ $substring: string;
|
|
|
+ $length: string;
|
|
|
+ $uppercase: string;
|
|
|
+ $lowercase: string;
|
|
|
+ $charAt: string;
|
|
|
+ $isReal: string;
|
|
|
+ $isInt: string;
|
|
|
+ $isBool: string;
|
|
|
+ $castReal: string;
|
|
|
+ $castInt: string;
|
|
|
+ $castBool: string;
|
|
|
+ $castString: string;
|
|
|
+ $castChar: string;
|
|
|
+ $sin: string;
|
|
|
+ $cos: string;
|
|
|
+ $tan: string;
|
|
|
+ $sqrt: string;
|
|
|
+ $pow: string;
|
|
|
+ $log: string;
|
|
|
+ $abs: string;
|
|
|
+ $negate: string;
|
|
|
+ $invert: string;
|
|
|
+ $max: string;
|
|
|
+ $min: string;
|
|
|
+ $rand: string;
|
|
|
+}
|
|
|
+
|
|
|
+export interface I18nLexer {
|
|
|
+ commands: I18N_LEXER_COMMANDS;
|
|
|
+ logicOp: I18N_LEXER_LOGICOP;
|
|
|
+ boolVal: I18N_LEXER_BOOLVAL;
|
|
|
+ types: I18N_LEXER_TYPES;
|
|
|
+ langLibs: I18N_LANG_LIBS;
|
|
|
+ langFuncs: I18N_LANG_FUNCS;
|
|
|
+}
|
|
|
+
|
|
|
+// General Regex Rules
|
|
|
+const ID = "[a-zA-Z_][a-zA-Z0-9_]*";
|
|
|
+const HEX_DIGIT = "[0-9a-fA-F]";
|
|
|
+const OCTAL_DIGIT = "[0-7]";
|
|
|
+const ESC_OCTAL = RegExp(
|
|
|
+ `\\\\[0-3]${OCTAL_DIGIT}${OCTAL_DIGIT}|\\\\${OCTAL_DIGIT}${OCTAL_DIGIT}|\\\\${OCTAL_DIGIT}`
|
|
|
+);
|
|
|
+const ESC_UNICODE = RegExp(
|
|
|
+ `\\\\u${HEX_DIGIT}${HEX_DIGIT}${HEX_DIGIT}${HEX_DIGIT}`
|
|
|
+);
|
|
|
+const ESC_SEQ_BASE = /\\[b,t,n,f,r,",',\\]|/;
|
|
|
+const ESC_SEQ = combineRegex`${ESC_SEQ_BASE}|${ESC_UNICODE}|${ESC_OCTAL}`;
|
|
|
+const STRING_CHARACTER = combineRegex`${ESC_SEQ}|[^"\\\\]`;
|
|
|
+
|
|
|
+export class IVProgLexer {
|
|
|
+ constructor(
|
|
|
+ private RKs: Record<string, string>,
|
|
|
+ public lexer: moo.Lexer,
|
|
|
+ private i18nLexer: I18nLexer
|
|
|
+ ) {}
|
|
|
+
|
|
|
+ getReservedKeys(): Record<string, string> {
|
|
|
+ return this.RKs;
|
|
|
+ }
|
|
|
+
|
|
|
+ getTypeKeys() {
|
|
|
+ return this.i18nLexer.types;
|
|
|
+ }
|
|
|
+
|
|
|
+ getLangLibs() {
|
|
|
+ return this.i18nLexer.langLibs;
|
|
|
+ }
|
|
|
+
|
|
|
+ getLangFuncs() {
|
|
|
+ return this.i18nLexer.langFuncs;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export function makeLexer(lexer: I18nLexer): IVProgLexer {
|
|
|
+ const RKs: Record<string, string> = {};
|
|
|
+ Object.entries(lexer.types).forEach(([key, value]) => (RKs[key] = value));
|
|
|
+ Object.entries(lexer.boolVal).forEach(([key, value]) => (RKs[key] = value));
|
|
|
+ Object.entries(lexer.commands).forEach(([key, value]) => (RKs[key] = value));
|
|
|
+ RKs["AND_OPERATOR"] = lexer.logicOp.RK_LOGICAL_AND;
|
|
|
+ RKs["OR_OPERATOR"] = lexer.logicOp.RK_LOGICAL_OR;
|
|
|
+ RKs["NOT_OPERATOR"] = lexer.logicOp.RK_LOGICAL_NOT;
|
|
|
+
|
|
|
+ const RESERVED_KEYS = moo.keywords(RKs);
|
|
|
+
|
|
|
+ const moolexer = moo.compile({
|
|
|
+ OPEN_BRACE: /\[/,
|
|
|
+ CLOSE_BRACE: /\]/,
|
|
|
+ OPEN_PARENTHESIS: /\(/,
|
|
|
+ CLOSE_PARENTHESIS: /\)/,
|
|
|
+ OPEN_CURLY: /\{/,
|
|
|
+ CLOSE_CURLY: /\}/,
|
|
|
+ COMMA: /,/,
|
|
|
+ ASSIGNMENT: /<-|←/,
|
|
|
+ REAL: /[0-9]+\.[0-9]*[eE][+-]?[0-9]+|[0-9]+\.[0-9]+/,
|
|
|
+ INTEGER: RegExp(`(?:0x|0X)${HEX_DIGIT}+|(?:0b|0B)[0-1]+|[0-9]+`),
|
|
|
+ SUM_OP: /[+-]/,
|
|
|
+ MULTI_OP: /[*/%]/,
|
|
|
+ RELATIONAL_OPERATOR: />=|==|<=|>|<|!=/,
|
|
|
+ COLON: /:/,
|
|
|
+ STRING: combineRegex`"(?:${STRING_CHARACTER})*?"`,
|
|
|
+ CHARACTER: combineRegex`'(?:${ESC_SEQ}|[^'\\\\])'`,
|
|
|
+ // LIB_ID: RegExp(`${ID}\.${ID}`), cannot filter keywords here
|
|
|
+ EOS: { match: /;\r?\n?|[\r\n]+/, lineBreaks: true },
|
|
|
+ WHITESPACE: /(?: |\t)+/,
|
|
|
+ COMMENTS: { match: /\/\/[^$]*?$|\/\*[^$]*?\*\//, lineBreaks: true },
|
|
|
+ RK_REFERENCE: /&/,
|
|
|
+ ID: { match: RegExp(ID), type: RESERVED_KEYS },
|
|
|
+ DOT: /\./,
|
|
|
+ });
|
|
|
+ return new IVProgLexer(RKs, moolexer, lexer);
|
|
|
+}
|