UNPKG

force-lang

Version:

a modern forth lang compatible with NodeJS

442 lines (413 loc) 9.87 kB
import log from 'bunny-logger'; import JSON5 from 'json5'; import TokenStream from './token-stream.js'; class Read { tokenize(e) { return new TokenStream(e.split('')); } is_delimiter(e) { if (this.is_whitespace(e) || this.is_eof(e)) { return true; } return false; } is_whitespace(e) { const ws = [' ', '\n', '\r', '\t']; return ws.includes(e); } is_digit(e) { if (this.is_delimiter(e)) { return false; } const num = e - '0'; return !!(num >= 0 && num <= 9); } is_comment(e) { if (e.peek() == '#' && e.lookahead(1) == '!') { return true; } if (e.peek() == '/' && e.lookahead(1) == '/') { return true; this.eat_line_comment(e); } if (e.peek() == '\\') { return true; } if (e.peek() == '-' && e.lookahead(1) == '-') { return true; } return false; } is_hex_digit(e) { if (this.is_eof(e)) { return false; } const hex_letters = ['a', 'b,', 'c', 'd', 'e', 'f']; if (this.is_digit(e) || hex_letters.includes(e.toLowerCase())) { return true; } return false; } is_bin_digit(e) { if (this.is_eof(e)) { return false; } return !!(e == 0 || e == 1); } is_num(e) { if ( (e.peek() == '+' && this.is_digit(e.lookahead(1))) || (e.peek() == '-' && this.is_digit(e.lookahead(1))) || this.is_digit(e.peek()) ) { return true; } return false; } is_eof(e) { return e === false; } is_bool(e) { if (e.peek() == 'T' || e.peek() == 'F') { if (this.is_delimiter(e.lookahead(1))) { return true; } } return false; } eat_whitespaces(e) { try { while (this.is_whitespace(e.peek())) { e.advance(); } } catch (e) {} } is_string(e) { return e == '"'; } is_string_single_quote(e) { return e == "'"; } is_json(e) { return !!(e === '{' || e === '['); } eat_line_comment(e) { while (e.peek() != '\n' && e.peek() != '\r' && !this.is_eof(e.peek())) { e.advance(); } this.eat_whitespaces(e); } eat_comments(e) { if (e.peek() == '#' && e.lookahead(1) == '!') { this.eat_line_comment(e); } if (e.peek() == '/' && e.lookahead(1) == '/') { this.eat_line_comment(e); } if (e.peek() == '\\') { this.eat_line_comment(e); } if (e.peek() == '-' && e.lookahead(1) == '-') { this.eat_line_comment(e); } if (e.peek() == '/' && e.lookahead(1) == '*') { while (e.peek() != '*' || e.lookahead(1) != '/') { if (this.is_eof(e.peek())) { return; } e.advance(); } e.advance(); e.advance(); } } eat_string(e) { let str = ''; try { if (this.is_string(e.peek())) { e.advance(); } while (!this.is_string(e.peek())) { if (e.peek() == '\\') { switch (e.lookahead(1)) { case 'n': str += '\n'; e.advance(); e.advance(); break; case 'r': str += '\r'; e.advance(); e.advance(); break; case '\\': str += '\\'; e.advance(); e.advance(); break; case '"': str += '"'; e.advance(); e.advance(); break; default: str += e.advance(); break; } } else { str += e.advance(); } } e.advance(); } catch (e) {} return str; } eat_string_single_quote(e) { let str = ''; try { if (this.is_string_single_quote(e.peek())) { e.advance(); } while (!this.is_string_single_quote(e.peek())) { if (e.peek() == '\\') { switch (e.lookahead(1)) { case 'n': str += '\n'; e.advance(); e.advance(); break; case 'r': str += '\r'; e.advance(); e.advance(); break; case '\\': str += '\\'; e.advance(); e.advance(); break; case '"': str += '"'; e.advance(); e.advance(); break; default: str += e.advance(); break; } } else { str += e.advance(); } } e.advance(); } catch (e) {} return str; } eat_number(e) { let sign = 1; let num = 0; let fract = false; let fract_num = 0; try { if (e.peek() == '-') { sign = -1; e.advance(); } if (e.peek() == '0' && e.lookahead(1) == 'x') { return sign * this.eat_hex(e); } if (e.peek() == '0' && e.lookahead(1) == 'b') { return sign * this.eat_bin(e); } while (!this.is_delimiter(e.peek())) { this.eat_comments(e); if (!fract && this.is_digit(e.peek())) { num = num * 10 + (e.advance() - '0'); } else if (fract && this.is_digit(e.peek())) { fract_num = fract_num * 10 + (e.advance() - '0'); } else if (e.peek() == '.') { fract = true; e.advance(); } else { log.error('syntax error. invalid number'); e.print_err(); process.exit(1); } } } catch (e) {} return fract ? sign * parseFloat(`${num}.${fract_num}`) : sign * num; } eat_hex(e) { let str = ''; try { if (e.peek() == '0' && e.lookahead(1) == 'x') { e.advance(); e.advance(); } while (!this.is_delimiter(e.peek())) { this.eat_comments(e); if (this.is_hex_digit(e.peek())) { str += e.advance(); } else { log.error('syntax error. invalid number'); e.print_err(); process.exit(1); } } } catch (e) {} return parseInt(str, 16); } eat_bin(e) { let str = ''; try { if (e.peek() == '0' && e.lookahead(1) == 'b') { e.advance(); e.advance(); } while (!this.is_delimiter(e.peek())) { this.eat_comments(e); if (this.is_bin_digit(e.peek())) { str += e.advance(); } else { log.error('syntax error. invalid number'); e.print_err(); process.exit(1); } } } catch (e) {} return parseInt(str, 2); } eat_bool(e) { let str = ''; try { while (!this.is_delimiter(e.peek())) { this.eat_comments(e); str += e.advance(); } } catch (e) {} return str; } eat_word(e) { let str = ''; try { while (!this.is_delimiter(e.peek())) { this.eat_comments(e); str += e.advance(); } } catch (e) {} return str; } eat_json(e) { if (!this.is_json(e.peek())) { return '{}'; } let opening_char; let closing_char; if (e.peek() === '{') { opening_char = '{'; closing_char = '}'; } if (e.peek() === '[') { opening_char = '['; closing_char = ']'; } let str = ''; let level = 0; const instring = 0; const json_line = e._line; const json_col = e._col; do { this.eat_comments(e); let x = e.advance(); // if(x==='"' || x==="'") instring=instring++ mod 2 if (x === '"' || x === "'") { const closing_str = x; let c; let sstr = ''; while ((c = e.advance()) != closing_str) { sstr += c; } x = `${closing_str}${sstr}${closing_str}`; } if (x === opening_char) { level++; } if (x === closing_char) { level--; } str += x; } while (e.peek() !== false && level != 0 /* && x!==closing_char */); // log.info(str); try { return JSON5.parse(str); } catch (err) { log.error(`error in json obj at line ${json_line}, col ${json_col}`); if (e._filename) { log.error(`in ${e._filename} file`); } log.error(err); } } where(e) { return { file: e._filename, line: e._line, col: e._col }; } read(e, filename = null) { try { if (filename) { e.set_filename(filename); } // if(this.is_whitespace(e)) {this.eat_whitespaces(e);return {"_type":"TC_NOP", "_where": this.where(e), "_datum": null}} this.eat_whitespaces(e); if (this.is_comment(e)) { this.eat_comments(e); return { _type: 'TC_NOP', _where: this.where(e), _datum: null }; } if (this.is_eof(e.peek())) { return false; } if (this.is_num(e)) { return { _type: 'TC_NUM', _where: this.where(e), _datum: this.eat_number(e), }; } if (this.is_string(e.peek())) { return { _type: 'TC_STR', _where: this.where(e), _datum: this.eat_string(e), }; } if (this.is_string_single_quote(e.peek())) { return { _type: 'TC_STR', _where: this.where(e), _datum: this.eat_string_single_quote(e), }; } if (this.is_json(e.peek())) { return { _type: 'TC_JSON', _where: this.where(e), _datum: this.eat_json(e), }; } if (this.is_bool(e)) { return { _type: 'TC_BOOL', _where: this.where(e), _datum: this.eat_bool(e), }; } return { _type: 'TC_WORD', _where: this.where(e), _datum: this.eat_word(e), }; } catch (e) { return false; } } } export default new Read();