sellquiz
Version:
An open source domain-specific language for online assessment
267 lines • 9.61 kB
JavaScript
/******************************************************************************
* SELL - SIMPLE E-LEARNING LANGUAGE *
* *
* Copyright (c) 2019-2021 TH Köln *
* Author: Andreas Schwenk, contact@compiler-construction.com *
* *
* Partly funded by: Digitale Hochschule NRW *
* https://www.dh.nrw/kooperationen/hm4mint.nrw-31 *
* *
* GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 *
* *
* This library is licensed as described in LICENSE, which you should have *
* received as part of this distribution. *
* *
* This software is distributed on "AS IS" basis, WITHOUT WARRENTY OF ANY *
* KIND, either impressed or implied. *
******************************************************************************/
export class SellToken {
constructor(str, line, col) {
this.str = str;
this.line = line; // line number
this.col = col; // column number
}
} // end of class SellToken
export class Lexer {
static isAlpha(ch) {
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || ch == '_' || ch == 'Ä' || ch == 'Ö' || ch == 'Ü' || ch == 'ß' || ch == 'ä' || ch == 'ö' || ch == 'ü';
}
static isNum(ch) {
return ch >= '1' && ch <= '9';
}
static isNum0(ch) {
return ch == '0' || this.isNum(ch);
}
static isIdentifier(str) {
for (let i = 0; i < str.length; i++) {
let ch = str[i];
if (i == 0) {
if (this.isAlpha(ch) == false)
return false;
}
else {
if (this.isAlpha(ch) == false && this.isNum0(ch) == false)
return false;
}
}
return true;
}
// integer = [ "-"], num { num0 };
static isInteger(str) {
// TODO: e.g. "0123" must return false TODO: parseUnary uses this function including leading zeros...
if (str.length == 0)
return false;
let startIdx = 0;
if (str[0] === '-') {
startIdx = 1;
if (str.length == 1)
return false;
}
for (let i = startIdx; i < str.length; i++) {
let ch = str[i];
if (this.isNum0(ch) == false)
return false;
}
return true;
}
static isReal(str) {
return isNaN(parseFloat(str)) == false;
}
static tokenize(s) {
let tokens = new Array();
let str = '';
let str_col = 1;
let lineIdx = 1;
let colIdx = 1;
let allowUnderscoredelimiter = !s.startsWith('\t') && !s.startsWith(' ');
//let allowUnderscoredelimiter = false;
for (let i = 0; i < s.length; i++) {
let ch = s[i];
let ch2 = '';
if (i < s.length - 1)
ch2 = s[i + 1];
let ch3 = '';
if (i < s.length - 2)
ch3 = s[i + 2];
switch (ch) {
case ' ':
case '\t':
case '\n':
if (str.length > 0)
tokens.push(new SellToken(str, lineIdx, str_col));
str = '';
str_col = colIdx;
if (ch === '\n') {
lineIdx++;
colIdx = 0;
}
else if (ch === ' ') {
tokens.push(new SellToken(ch, lineIdx, str_col));
}
break;
case '_':
if (allowUnderscoredelimiter) {
if (str.length > 0)
tokens.push(new SellToken(str, lineIdx, str_col));
str = '';
str_col = colIdx;
if (ch == '_' && ch2 == '_') {
ch = "__";
i++;
}
tokens.push(new SellToken(ch, lineIdx, str_col));
str_col = colIdx;
}
else {
if (str.length == 0)
str_col = colIdx;
str += ch;
}
break;
case '(':
case ')':
case '{':
case '}':
case '[':
case ']':
case '+':
case '-':
case '*':
case '/':
case '^':
case '~':
case '#':
case '.':
case ',':
case ';':
case ':':
case '=':
case '@':
case '|':
case '$':
case '?':
case '!':
case '"':
case '<':
case '>':
case '`':
case '\\':
case '\'':
if (str.length > 0)
tokens.push(new SellToken(str, lineIdx, str_col));
str = '';
str_col = colIdx;
if (ch == ':' && ch2 == '=') {
ch = ":=";
i++;
}
else if (ch == '^' && ch2 == '^') {
ch = "^^";
i++;
}
else if (ch == '\\' && ch2 == '\\') {
ch = "\\\\";
i++;
}
else if (ch == '-' && ch2 == '>') {
ch = "->";
i++;
}
else if (ch == '|' && ch2 == '-' && ch3 == '>') {
ch = "|->";
i += 2;
}
else if (ch == '<' && ch2 == '=') {
ch = "<=";
i++;
}
else if (ch == '>' && ch2 == '=') {
ch = ">=";
i++;
}
else if (ch == '=' && ch2 == '=') {
ch = "==";
i++;
}
else if (ch == '!' && ch2 == '=') {
ch = "!=";
i++;
}
else if (ch == '.' && ch2 == '.' && ch3 == '.') {
ch = "...";
i += 2;
}
else if (ch == '`' && ch2 == '`' && ch3 == '`') {
ch = "```";
i += 2;
}
tokens.push(new SellToken(ch, lineIdx, str_col));
str_col = colIdx;
break;
default:
if (str.length == 0)
str_col = colIdx;
str += ch;
}
colIdx++;
}
if (str.length > 0)
tokens.push(new SellToken(str, lineIdx, str_col));
//for(let i=0; i<tokens.length; i++) {
// console.log(tokens[i]);
//}
return tokens;
}
static printTokenList(tokens) {
for (let i = 0; i < tokens.length; i++) {
let token = tokens[i];
console.log(token.line + ':' + token.col + ':' + token.str);
}
}
static randomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min) + min);
}
static splitStringAndKeepDelimiters(s, del) {
let tokens = Array();
let tk = '';
for (let i = 0; i < s.length; i++) {
let match = true;
let match_d = '';
for (let j = 0; j < del.length; j++) {
let d = del[j];
match = true;
if ((i + d.length) > s.length)
match = false;
else {
for (let k = 0; k < d.length; k++) {
if (s[i + k] != d[k]) {
match = false;
break;
}
}
}
if (match) {
match_d = d;
break;
}
}
if (match) {
if (tk.length > 0) {
tokens.push(tk);
tk = '';
}
tokens.push(match_d);
i += match_d.length - 1;
}
else {
tk += s[i];
}
}
if (tk.length > 0)
tokens.push(tk);
return tokens;
}
} // end of class Lexer
//# sourceMappingURL=lex.js.map