|
@ -0,0 +1,957 @@ |
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
|
|
|
|
#include "gp/containers/array.hpp"
|
|
|
|
|
|
#include "gp/containers/vector.hpp"
|
|
|
|
|
|
#include "gp/text/ascii.hpp"
|
|
|
|
|
|
|
|
|
|
|
|
using string = gp::vector<char>; |
|
|
|
|
|
|
|
|
|
|
|
#define FOREACH_LEXEME_TYPE \
|
|
|
|
|
|
LEX(leftBracket) \ |
|
|
|
|
|
LEX(rightBracket) \ |
|
|
|
|
|
LEX(leftParenthesis) \ |
|
|
|
|
|
LEX(rightParenthesis) \ |
|
|
|
|
|
LEX(leftCurlyBrace) \ |
|
|
|
|
|
LEX(rightCurlyBrace) \ |
|
|
|
|
|
LEX(period) \ |
|
|
|
|
|
LEX(semicolon) \ |
|
|
|
|
|
LEX(colon) \ |
|
|
|
|
|
LEX(doubleColon) \ |
|
|
|
|
|
LEX(comma) \ |
|
|
|
|
|
LEX(at) \ |
|
|
|
|
|
LEX(pointer) \ |
|
|
|
|
|
LEX(as) \ |
|
|
|
|
|
LEX(try_) \ |
|
|
|
|
|
LEX(catch_) \ |
|
|
|
|
|
LEX(raise_) \ |
|
|
|
|
|
LEX(defer) \ |
|
|
|
|
|
LEX(assign) \ |
|
|
|
|
|
LEX(addAssign) \ |
|
|
|
|
|
LEX(substractAssign) \ |
|
|
|
|
|
LEX(multiplyAssign) \ |
|
|
|
|
|
LEX(divideAssign) \ |
|
|
|
|
|
LEX(concatenateAssign) \ |
|
|
|
|
|
LEX(remainderAssign) \ |
|
|
|
|
|
LEX(powerAssign) \ |
|
|
|
|
|
LEX(plus) \ |
|
|
|
|
|
LEX(minus) \ |
|
|
|
|
|
LEX(add) \ |
|
|
|
|
|
LEX(substract) \ |
|
|
|
|
|
LEX(multiply) \ |
|
|
|
|
|
LEX(divide) \ |
|
|
|
|
|
LEX(concatenate) \ |
|
|
|
|
|
LEX(remainder) \ |
|
|
|
|
|
LEX(power) \ |
|
|
|
|
|
LEX(equal) \ |
|
|
|
|
|
LEX(doubleEqual) \ |
|
|
|
|
|
LEX(threeWayComparison) \ |
|
|
|
|
|
LEX(notEqual) \ |
|
|
|
|
|
LEX(greaterOrEqual) \ |
|
|
|
|
|
LEX(greater) \ |
|
|
|
|
|
LEX(lesserOrEqual) \ |
|
|
|
|
|
LEX(lesser) \ |
|
|
|
|
|
LEX(leftShift) \ |
|
|
|
|
|
LEX(rightShift) \ |
|
|
|
|
|
LEX(and_) \ |
|
|
|
|
|
LEX(or_) \ |
|
|
|
|
|
LEX(xor_) \ |
|
|
|
|
|
LEX(not_) \ |
|
|
|
|
|
LEX(increment) \ |
|
|
|
|
|
LEX(decrement) \ |
|
|
|
|
|
LEX(identifier) \ |
|
|
|
|
|
LEX(integer) \ |
|
|
|
|
|
LEX(float_) \ |
|
|
|
|
|
LEX(boolean) \ |
|
|
|
|
|
LEX(string_) \ |
|
|
|
|
|
LEX(null_) \ |
|
|
|
|
|
LEX(public_) \ |
|
|
|
|
|
LEX(main_) \ |
|
|
|
|
|
LEX(type_) \ |
|
|
|
|
|
LEX(event_) \ |
|
|
|
|
|
LEX(class_) \ |
|
|
|
|
|
LEX(enum_) \ |
|
|
|
|
|
LEX(template_) \ |
|
|
|
|
|
LEX(new_) \ |
|
|
|
|
|
LEX(copy) \ |
|
|
|
|
|
LEX(send) \ |
|
|
|
|
|
LEX(receive) \ |
|
|
|
|
|
LEX(intType) \ |
|
|
|
|
|
LEX(floatType) \ |
|
|
|
|
|
LEX(boolType) \ |
|
|
|
|
|
LEX(stringType) \ |
|
|
|
|
|
LEX(arrayType) \ |
|
|
|
|
|
LEX(functionType) \ |
|
|
|
|
|
LEX(taskType) \ |
|
|
|
|
|
LEX(chanType) \ |
|
|
|
|
|
LEX(autoType) \ |
|
|
|
|
|
LEX(if_) \ |
|
|
|
|
|
LEX(unless) \ |
|
|
|
|
|
LEX(else_) \ |
|
|
|
|
|
LEX(switch_) \ |
|
|
|
|
|
LEX(select) \ |
|
|
|
|
|
LEX(case_) \ |
|
|
|
|
|
LEX(while_) \ |
|
|
|
|
|
LEX(do_) \ |
|
|
|
|
|
LEX(until) \ |
|
|
|
|
|
LEX(for_) \ |
|
|
|
|
|
LEX(loop) \ |
|
|
|
|
|
LEX(return_) \ |
|
|
|
|
|
LEX(self) \ |
|
|
|
|
|
LEX(kill) \ |
|
|
|
|
|
LEX(killAll) \ |
|
|
|
|
|
LEX(yield) \ |
|
|
|
|
|
LEX(break_) \ |
|
|
|
|
|
LEX(continue_) |
|
|
|
|
|
|
|
|
|
|
|
#define LEX(x) x,
|
|
|
|
|
|
enum class gr_lexeme_type { |
|
|
|
|
|
FOREACH_LEXEME_TYPE |
|
|
|
|
|
}; |
|
|
|
|
|
#undef LEX
|
|
|
|
|
|
|
|
|
|
|
|
#define LEX(x) case gr_lexeme_type::x: return #x;
|
|
|
|
|
|
constexpr inline const char* to_string(const gr_lexeme_type& value) { |
|
|
|
|
|
switch(value) { |
|
|
|
|
|
FOREACH_LEXEME_TYPE |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
#undef LEX
|
|
|
|
|
|
|
|
|
|
|
|
#define LEX(x) +1
|
|
|
|
|
|
constexpr inline const char* to_pretty_string(const gr_lexeme_type& value) { |
|
|
|
|
|
constexpr gp::array<const char*, 0 + FOREACH_LEXEME_TYPE> names = { |
|
|
|
|
|
"[", "]", "(", ")", "{", "}", ".", ";", ":", "::", ",", "@", "&", "as", |
|
|
|
|
|
"try", "catch", "raise", "defer", "=", "+=", "-=", "*=", "/=", "~=", |
|
|
|
|
|
"%=", "^=", "+", "-", "+", "-", "*", "/", "~", "%", "^", "==", "===", |
|
|
|
|
|
"<=>", "!=", ">=", ">", "<=", "<", "<<", ">>", "and", "or", "xor", "not", "++", |
|
|
|
|
|
"--", "identifier", "const_int", "const_float", "const_bool", |
|
|
|
|
|
"const_str", "null", "pub", "main", "type", "event", "class", "enum", |
|
|
|
|
|
"template", "new", "copy", "send", "receive", "int", "float", "bool", |
|
|
|
|
|
"string", "array", "func", "task", "chan", "let", "if", "unless", |
|
|
|
|
|
"else", "switch", "select", "case", "while", "do", "until", "for", "loop", |
|
|
|
|
|
"return", "self", "kill", "killall", "yield", "break", "continue" |
|
|
|
|
|
}; |
|
|
|
|
|
return names[(uint64_t)value]; |
|
|
|
|
|
} |
|
|
|
|
|
#undef LEX
|
|
|
|
|
|
|
|
|
|
|
|
struct gr_lexeme; |
|
|
|
|
|
|
|
|
|
|
|
class gr_lexer { |
|
|
|
|
|
public: |
|
|
|
|
|
using file_loader_t = gp::function<gp::vector<char>(const gp::vector<char>&)>; |
|
|
|
|
|
gp::allocator& _allocator; |
|
|
|
|
|
file_loader_t file_loader; |
|
|
|
|
|
private: |
|
|
|
|
|
gp::vector<string> _files_to_import, _files_imported, _lines; |
|
|
|
|
|
|
|
|
|
|
|
string _file, _text; |
|
|
|
|
|
uint64_t _line, _current, _position_of_line, _file_id; |
|
|
|
|
|
|
|
|
|
|
|
gp::vector<gr_lexeme> _lexemes; |
|
|
|
|
|
|
|
|
|
|
|
char get(ssize_t offset = 0); |
|
|
|
|
|
bool advance(bool start_from_current = false); |
|
|
|
|
|
void scan_script(); |
|
|
|
|
|
void scan_number(); |
|
|
|
|
|
void scan_string(); |
|
|
|
|
|
void scan_operator(); |
|
|
|
|
|
void scan_word(); |
|
|
|
|
|
void scan_file_path(); |
|
|
|
|
|
void scan_use(); |
|
|
|
|
|
string convert_path_to_import(string&); |
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
|
|
|
|
|
|
|
|
gr_lexer(gp::allocator& alloc, file_loader_t& loader) |
|
|
|
|
|
: _allocator(alloc) |
|
|
|
|
|
, file_loader(loader) |
|
|
|
|
|
, _files_to_import(_allocator) |
|
|
|
|
|
, _files_imported(_allocator) |
|
|
|
|
|
, _lines(_allocator) |
|
|
|
|
|
, _file(_allocator) |
|
|
|
|
|
, _text(_allocator) |
|
|
|
|
|
, _lexemes(_allocator) |
|
|
|
|
|
{} |
|
|
|
|
|
|
|
|
|
|
|
const gp::vector<gr_lexeme>& lexemes() { |
|
|
|
|
|
return _lexemes; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void scan_file(gp::vector<char>& file_name); |
|
|
|
|
|
|
|
|
|
|
|
const string& get_line(const gr_lexeme&) const; |
|
|
|
|
|
const string& get_file(const gr_lexeme&) const; |
|
|
|
|
|
const string& get_file(const size_t&) const; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
struct gr_lexeme { |
|
|
|
|
|
const gr_lexer& lexer; |
|
|
|
|
|
size_t _file_id; |
|
|
|
|
|
size_t _line, _column, _text_length = 1; |
|
|
|
|
|
|
|
|
|
|
|
gr_lexeme_type type; |
|
|
|
|
|
bool |
|
|
|
|
|
is_literal, |
|
|
|
|
|
is_operator, |
|
|
|
|
|
is_keyword, |
|
|
|
|
|
is_type; |
|
|
|
|
|
int ivalue; |
|
|
|
|
|
float fvalue; |
|
|
|
|
|
bool bvalue; |
|
|
|
|
|
string svalue; |
|
|
|
|
|
|
|
|
|
|
|
gr_lexeme(const gr_lexer& v) |
|
|
|
|
|
: lexer(v) |
|
|
|
|
|
, svalue(v._allocator) |
|
|
|
|
|
{} |
|
|
|
|
|
|
|
|
|
|
|
const string& get_line() const { |
|
|
|
|
|
return lexer.get_line(*this); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const string& get_file() { |
|
|
|
|
|
return lexer.get_file(*this); |
|
|
|
|
|
} |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
inline char gr_lexer::get(ssize_t offset) { |
|
|
|
|
|
const uint position = ssize_t(_current) + offset; |
|
|
|
|
|
gp_config::assertion(!(position < 0 || position >= _text.size()), "Unexpected end of script"); |
|
|
|
|
|
return _text[position]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline const string& gr_lexer::get_line(const gr_lexeme& lex) const { |
|
|
|
|
|
gp_config::assertion(!(lex._file_id >= _files_imported.size()), "Lexeme file id out of bounds"); |
|
|
|
|
|
// TODO: Implement this
|
|
|
|
|
|
return _files_imported[lex._file_id]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline const string& gr_lexer::get_file(const gr_lexeme& lex) const { |
|
|
|
|
|
gp_config::assertion(!(lex._file_id >= _files_imported.size()), "Lexeme file id out of bounds"); |
|
|
|
|
|
return _files_imported[lex._file_id]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline const string& gr_lexer::get_file(const size_t& file_id) const { |
|
|
|
|
|
gp_config::assertion(!(file_id >= _files_imported.size()), "File id out of bounds"); |
|
|
|
|
|
return _files_imported[file_id]; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline bool gr_lexer::advance(bool start_from_current) { |
|
|
|
|
|
if(!start_from_current) { |
|
|
|
|
|
_current++; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if(_current >= _text.size()) { |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
char symbol = _text[_current]; |
|
|
|
|
|
|
|
|
|
|
|
whileLoop: while(symbol <= 0x20 || symbol == '/' || symbol == '#') { |
|
|
|
|
|
if(_current >= _text.size()) { |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
symbol = _text[_current]; |
|
|
|
|
|
|
|
|
|
|
|
if(symbol == '\n') { |
|
|
|
|
|
_position_of_line = _current; |
|
|
|
|
|
_line++; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol == '#') |
|
|
|
|
|
{ |
|
|
|
|
|
do { |
|
|
|
|
|
if(_current >= _text.size()) return false; |
|
|
|
|
|
_current++; |
|
|
|
|
|
} while (_text[_current] != '\n'); |
|
|
|
|
|
_position_of_line = _current; |
|
|
|
|
|
_line++; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol == '/') |
|
|
|
|
|
{ |
|
|
|
|
|
if((_current + 1) >= _text.size()) { |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
switch(_text[_current + 1]) { |
|
|
|
|
|
case '/': { |
|
|
|
|
|
do { |
|
|
|
|
|
if(_current >= _text.size()) { |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
} while(_current < _text.size() && _text[_current] != '\n'); |
|
|
|
|
|
_position_of_line = _current; |
|
|
|
|
|
_line++; |
|
|
|
|
|
}break; |
|
|
|
|
|
case '*': { |
|
|
|
|
|
for(;;) { |
|
|
|
|
|
if((_current + 1) >= _text.size()) { |
|
|
|
|
|
_current++; |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if(_text[_current] == '\n') { |
|
|
|
|
|
_position_of_line = _current; |
|
|
|
|
|
_line++; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if(_text[_current] == '*' && _text[_current + 1] == '/') { |
|
|
|
|
|
_current++; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
_current++; |
|
|
|
|
|
} |
|
|
|
|
|
}break; |
|
|
|
|
|
default: |
|
|
|
|
|
// Goto honorable
|
|
|
|
|
|
goto whileLoop; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
_current++; |
|
|
|
|
|
|
|
|
|
|
|
if(_current >= _text.size()) { |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
symbol = _text[_current]; |
|
|
|
|
|
} |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline void gr_lexer::scan_script() { |
|
|
|
|
|
advance(true); |
|
|
|
|
|
|
|
|
|
|
|
constexpr static auto is_operator = [](char v) { |
|
|
|
|
|
if(v == '!') return true; |
|
|
|
|
|
if(v >= '#' && v <='&') return true; |
|
|
|
|
|
if(v >= '(' && v <='-') return true; |
|
|
|
|
|
if(v == '/') return true; |
|
|
|
|
|
if(v >= ':' && v <='@') return true; |
|
|
|
|
|
if(v >= '[' && v <='^') return true; |
|
|
|
|
|
if(v >= '{' && v <='~') return true; |
|
|
|
|
|
return false; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
do { |
|
|
|
|
|
if (_current >= _text.size()) |
|
|
|
|
|
break; |
|
|
|
|
|
auto c = get(); |
|
|
|
|
|
if(is_digit(c)) scan_number(); |
|
|
|
|
|
else if(c == '.') { |
|
|
|
|
|
if (get(1) >= '0' && get(1) <= '9') |
|
|
|
|
|
scan_number(); |
|
|
|
|
|
else |
|
|
|
|
|
scan_operator(); |
|
|
|
|
|
} |
|
|
|
|
|
else if(is_operator(c)) scan_operator(); |
|
|
|
|
|
else if(c == '\"') scan_string(); |
|
|
|
|
|
else scan_word(); |
|
|
|
|
|
}while (advance()); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline void gr_lexer::scan_number(){ |
|
|
|
|
|
gr_lexeme lex = gr_lexeme(*this); |
|
|
|
|
|
lex.is_literal = true; |
|
|
|
|
|
|
|
|
|
|
|
bool isFloat; |
|
|
|
|
|
string buffer(_allocator); |
|
|
|
|
|
for (;;) { |
|
|
|
|
|
char symbol = get(); |
|
|
|
|
|
|
|
|
|
|
|
if (symbol >= '0' && symbol <= '9') |
|
|
|
|
|
buffer.push_back(symbol); |
|
|
|
|
|
else if (symbol == '_') { |
|
|
|
|
|
// Do nothing, only cosmetic (e.g. 1_000_000).
|
|
|
|
|
|
} |
|
|
|
|
|
else if (symbol == '.') { |
|
|
|
|
|
if (isFloat) |
|
|
|
|
|
break; |
|
|
|
|
|
isFloat = true; |
|
|
|
|
|
buffer.push_back(symbol); |
|
|
|
|
|
} |
|
|
|
|
|
else if (symbol == 'f') { |
|
|
|
|
|
isFloat = true; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
if (_current) |
|
|
|
|
|
_current--; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
_current++; |
|
|
|
|
|
|
|
|
|
|
|
if (_current >= _text.size()) |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline void gr_lexer::scan_string(){ |
|
|
|
|
|
gr_lexeme lex = gr_lexeme(*this); |
|
|
|
|
|
lex.type = gr_lexeme_type::string_; |
|
|
|
|
|
lex.is_literal = true; |
|
|
|
|
|
|
|
|
|
|
|
gp_config::assertion(get() != '\"',"Expected \'\"\' at the beginning of the string."); |
|
|
|
|
|
_current++; |
|
|
|
|
|
|
|
|
|
|
|
string buffer(_allocator); |
|
|
|
|
|
bool escape = false; |
|
|
|
|
|
bool wasEscape = false; |
|
|
|
|
|
for (;;) { |
|
|
|
|
|
gp_config::assertion(_current >= _text.size(),"Missing \'\"\' character."); |
|
|
|
|
|
char symbol = get(); |
|
|
|
|
|
|
|
|
|
|
|
if (symbol == '\n') { |
|
|
|
|
|
_position_of_line = _current; |
|
|
|
|
|
_line++; |
|
|
|
|
|
} |
|
|
|
|
|
else if (symbol == '\"' && (!wasEscape)) |
|
|
|
|
|
break; |
|
|
|
|
|
else if (symbol == '\\' && (!wasEscape)) { |
|
|
|
|
|
escape = true; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!escape) { |
|
|
|
|
|
if (!wasEscape) { |
|
|
|
|
|
buffer.push_back(symbol); |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
if (symbol == 'n') |
|
|
|
|
|
buffer.push_back('\n'); |
|
|
|
|
|
else |
|
|
|
|
|
buffer.push_back(symbol); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
wasEscape = escape; |
|
|
|
|
|
escape = false; |
|
|
|
|
|
|
|
|
|
|
|
_current++; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
lex._text_length = size_t(buffer.size() + 2u); |
|
|
|
|
|
lex.svalue = buffer; |
|
|
|
|
|
_lexemes.push_back(lex); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline void gr_lexer::scan_operator(){ |
|
|
|
|
|
gr_lexeme lex = gr_lexeme(*this); |
|
|
|
|
|
lex.is_operator = true; |
|
|
|
|
|
|
|
|
|
|
|
switch (get()) { |
|
|
|
|
|
case '{': |
|
|
|
|
|
lex.type = gr_lexeme_type::leftCurlyBrace; |
|
|
|
|
|
break; |
|
|
|
|
|
case '}': |
|
|
|
|
|
lex.type = gr_lexeme_type::rightCurlyBrace; |
|
|
|
|
|
break; |
|
|
|
|
|
case '(': |
|
|
|
|
|
lex.type = gr_lexeme_type::leftParenthesis; |
|
|
|
|
|
break; |
|
|
|
|
|
case ')': |
|
|
|
|
|
lex.type = gr_lexeme_type::rightParenthesis; |
|
|
|
|
|
break; |
|
|
|
|
|
case '[': |
|
|
|
|
|
lex.type = gr_lexeme_type::leftBracket; |
|
|
|
|
|
break; |
|
|
|
|
|
case ']': |
|
|
|
|
|
lex.type = gr_lexeme_type::rightBracket; |
|
|
|
|
|
break; |
|
|
|
|
|
case '.': |
|
|
|
|
|
lex.type = gr_lexeme_type::period; |
|
|
|
|
|
break; |
|
|
|
|
|
case ';': |
|
|
|
|
|
lex.type = gr_lexeme_type::semicolon; |
|
|
|
|
|
break; |
|
|
|
|
|
case ':': |
|
|
|
|
|
lex.type = gr_lexeme_type::colon; |
|
|
|
|
|
if (_current + 1 >= _text.size()) |
|
|
|
|
|
break; |
|
|
|
|
|
if (get(1) == ':') { |
|
|
|
|
|
lex.type = gr_lexeme_type::doubleColon; |
|
|
|
|
|
lex._text_length = 2; |
|
|
|
|
|
_current++; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case ',': |
|
|
|
|
|
lex.type = gr_lexeme_type::comma; |
|
|
|
|
|
break; |
|
|
|
|
|
case '^': |
|
|
|
|
|
lex.type = gr_lexeme_type::power; |
|
|
|
|
|
if (_current + 1 >= _text.size()) |
|
|
|
|
|
break; |
|
|
|
|
|
if (get(1) == '=') { |
|
|
|
|
|
lex.type = gr_lexeme_type::powerAssign; |
|
|
|
|
|
lex._text_length = 2; |
|
|
|
|
|
_current++; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case '@': |
|
|
|
|
|
lex.type = gr_lexeme_type::at; |
|
|
|
|
|
break; |
|
|
|
|
|
case '&': |
|
|
|
|
|
lex.type = gr_lexeme_type::pointer; |
|
|
|
|
|
break; |
|
|
|
|
|
case '~': |
|
|
|
|
|
lex.type = gr_lexeme_type::concatenate; |
|
|
|
|
|
if (_current + 1 >= _text.size()) |
|
|
|
|
|
break; |
|
|
|
|
|
if (get(1) == '=') { |
|
|
|
|
|
lex.type = gr_lexeme_type::concatenateAssign; |
|
|
|
|
|
lex._text_length = 2; |
|
|
|
|
|
_current++; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case '+': |
|
|
|
|
|
lex.type = gr_lexeme_type::add; |
|
|
|
|
|
if (_current + 1 >= _text.size()) |
|
|
|
|
|
break; |
|
|
|
|
|
switch (get(1)) { |
|
|
|
|
|
case '=': |
|
|
|
|
|
lex.type = gr_lexeme_type::addAssign; |
|
|
|
|
|
lex._text_length = 2; |
|
|
|
|
|
_current++; |
|
|
|
|
|
break; |
|
|
|
|
|
case '+': |
|
|
|
|
|
lex.type = gr_lexeme_type::increment; |
|
|
|
|
|
lex._text_length = 2; |
|
|
|
|
|
_current++; |
|
|
|
|
|
break; |
|
|
|
|
|
default: |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case '-': |
|
|
|
|
|
lex.type = gr_lexeme_type::substract; |
|
|
|
|
|
if (_current + 1 >= _text.size()) |
|
|
|
|
|
break; |
|
|
|
|
|
switch (get(1)) { |
|
|
|
|
|
case '=': |
|
|
|
|
|
lex.type = gr_lexeme_type::substractAssign; |
|
|
|
|
|
lex._text_length = 2; |
|
|
|
|
|
_current++; |
|
|
|
|
|
break; |
|
|
|
|
|
case '-': |
|
|
|
|
|
lex.type = gr_lexeme_type::decrement; |
|
|
|
|
|
lex._text_length = 2; |
|
|
|
|
|
_current++; |
|
|
|
|
|
break; |
|
|
|
|
|
default: |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case '*': |
|
|
|
|
|
lex.type = gr_lexeme_type::multiply; |
|
|
|
|
|
if (_current + 1 >= _text.size()) |
|
|
|
|
|
break; |
|
|
|
|
|
if (get(1) == '=') { |
|
|
|
|
|
lex.type = gr_lexeme_type::multiplyAssign; |
|
|
|
|
|
lex._text_length = 2; |
|
|
|
|
|
_current++; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case '/': |
|
|
|
|
|
lex.type = gr_lexeme_type::divide; |
|
|
|
|
|
if (_current + 1 >= _text.size()) |
|
|
|
|
|
break; |
|
|
|
|
|
if (get(1) == '=') { |
|
|
|
|
|
lex.type = gr_lexeme_type::divideAssign; |
|
|
|
|
|
lex._text_length = 2; |
|
|
|
|
|
_current++; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case '%': |
|
|
|
|
|
lex.type = gr_lexeme_type::remainder; |
|
|
|
|
|
if (_current + 1 >= _text.size()) |
|
|
|
|
|
break; |
|
|
|
|
|
if (get(1) == '=') { |
|
|
|
|
|
lex.type = gr_lexeme_type::remainderAssign; |
|
|
|
|
|
lex._text_length = 2; |
|
|
|
|
|
_current++; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case '=': |
|
|
|
|
|
lex.type = gr_lexeme_type::assign; |
|
|
|
|
|
if (_current + 1 >= _text.size()) |
|
|
|
|
|
break; |
|
|
|
|
|
if (get(1) == '=') { |
|
|
|
|
|
lex.type = gr_lexeme_type::equal; |
|
|
|
|
|
lex._text_length = 2; |
|
|
|
|
|
_current++; |
|
|
|
|
|
if (_current + 1 >= _text.size()) |
|
|
|
|
|
break; |
|
|
|
|
|
if (get(1) == '=') { |
|
|
|
|
|
lex.type = gr_lexeme_type::doubleEqual; |
|
|
|
|
|
lex._text_length = 3; |
|
|
|
|
|
_current++; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case '<': |
|
|
|
|
|
lex.type = gr_lexeme_type::lesser; |
|
|
|
|
|
if (_current + 1 >= _text.size()) |
|
|
|
|
|
break; |
|
|
|
|
|
if (get(1) == '=') { |
|
|
|
|
|
lex.type = gr_lexeme_type::lesserOrEqual; |
|
|
|
|
|
lex._text_length = 2; |
|
|
|
|
|
_current++; |
|
|
|
|
|
if (_current + 1 >= _text.size()) |
|
|
|
|
|
break; |
|
|
|
|
|
if (get(1) == '>') { |
|
|
|
|
|
lex.type = gr_lexeme_type::threeWayComparison; |
|
|
|
|
|
lex._text_length = 3; |
|
|
|
|
|
_current++; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
else if (get(1) == '-') { |
|
|
|
|
|
lex.type = gr_lexeme_type::send; |
|
|
|
|
|
lex._text_length = 2; |
|
|
|
|
|
_current++; |
|
|
|
|
|
} |
|
|
|
|
|
else if (get(1) == '<') { |
|
|
|
|
|
lex.type = gr_lexeme_type::leftShift; |
|
|
|
|
|
lex._text_length = 2; |
|
|
|
|
|
_current++; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case '>': |
|
|
|
|
|
lex.type = gr_lexeme_type::greater; |
|
|
|
|
|
if (_current + 1 >= _text.size()) |
|
|
|
|
|
break; |
|
|
|
|
|
if (get(1) == '=') { |
|
|
|
|
|
lex.type = gr_lexeme_type::greaterOrEqual; |
|
|
|
|
|
lex._text_length = 2; |
|
|
|
|
|
_current++; |
|
|
|
|
|
} |
|
|
|
|
|
else if (get(1) == '>') { |
|
|
|
|
|
lex.type = gr_lexeme_type::rightShift; |
|
|
|
|
|
lex._text_length = 2; |
|
|
|
|
|
_current++; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case '!': |
|
|
|
|
|
lex.type = gr_lexeme_type::not_; |
|
|
|
|
|
if (_current + 1 >= _text.size()) |
|
|
|
|
|
break; |
|
|
|
|
|
if (get(1) == '=') { |
|
|
|
|
|
lex.type = gr_lexeme_type::notEqual; |
|
|
|
|
|
lex._text_length = 2; |
|
|
|
|
|
_current++; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
default: |
|
|
|
|
|
gp_config::assertion(false, "GrLexer: invalid operator"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
_lexemes.push_back(lex); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
namespace _hidden { |
|
|
|
|
|
bool operator==(const gp::vector<char>& lhs, const char* rhs) { |
|
|
|
|
|
for(size_t index = 0; index < lhs.size() && rhs[index] != 0; index++){ |
|
|
|
|
|
if(lhs[index] != rhs[index]) return false; |
|
|
|
|
|
} |
|
|
|
|
|
return true; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline void gr_lexer::scan_word(){ |
|
|
|
|
|
gr_lexeme lex = gr_lexeme(*this); |
|
|
|
|
|
lex.is_keyword = true; |
|
|
|
|
|
|
|
|
|
|
|
string symbol_buffer(_allocator); |
|
|
|
|
|
for (;;) { |
|
|
|
|
|
if (_current >= _text.size()) |
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
char symbol = get(); |
|
|
|
|
|
if (symbol == '!' || symbol == '?') { |
|
|
|
|
|
symbol_buffer.push_back(symbol); |
|
|
|
|
|
_current++; |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
if (symbol <= '&' || (symbol >= '(' && symbol <= '/') || (symbol >= ':' |
|
|
|
|
|
&& symbol <= '@') || (symbol >= '[' && symbol <= '^') |
|
|
|
|
|
|| (symbol >= '{' && symbol <= 0x7F)) |
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
symbol_buffer.push_back(symbol); |
|
|
|
|
|
_current++; |
|
|
|
|
|
} |
|
|
|
|
|
_current--; |
|
|
|
|
|
|
|
|
|
|
|
lex._text_length = symbol_buffer.size(); |
|
|
|
|
|
|
|
|
|
|
|
using namespace _hidden; |
|
|
|
|
|
|
|
|
|
|
|
if(symbol_buffer == "use"){ |
|
|
|
|
|
scan_use(); |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "pub"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::public_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "main"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::main_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "type"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::type_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "event"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::event_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "class"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::class_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "enum"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::enum_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "template"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::template_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "if"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::if_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "unless"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::unless; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "else"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::else_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "switch"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::switch_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "select"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::select; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "case"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::case_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "while"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::while_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "do"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::do_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "until"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::until; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "for"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::for_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "loop"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::loop; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "return"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::return_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "self"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::self; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "kill"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::kill; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "killall"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::killAll; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "yield"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::yield; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "break"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::break_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "continue"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::continue_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "as"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::as; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "try"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::try_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "catch"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::catch_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "raise"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::raise_; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "defer"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::defer; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "task"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::taskType; |
|
|
|
|
|
lex.is_type = true; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "func"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::functionType; |
|
|
|
|
|
lex.is_type = true; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "int"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::intType; |
|
|
|
|
|
lex.is_type = true; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "float"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::floatType; |
|
|
|
|
|
lex.is_type = true; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "bool"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::boolType; |
|
|
|
|
|
lex.is_type = true; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "string"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::stringType; |
|
|
|
|
|
lex.is_type = true; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "array"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::arrayType; |
|
|
|
|
|
lex.is_type = true; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "chan"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::chanType; |
|
|
|
|
|
lex.is_type = true; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "new"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::new_; |
|
|
|
|
|
lex.is_type = false; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "let"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::autoType; |
|
|
|
|
|
lex.is_type = false; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "true"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::boolean; |
|
|
|
|
|
lex.is_keyword = false; |
|
|
|
|
|
lex.is_literal = true; |
|
|
|
|
|
lex.bvalue = true; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "false"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::boolean; |
|
|
|
|
|
lex.is_keyword = false; |
|
|
|
|
|
lex.is_literal = true; |
|
|
|
|
|
lex.bvalue = false; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "null"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::null_; |
|
|
|
|
|
lex.is_keyword = false; |
|
|
|
|
|
lex.is_literal = true; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "not"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::not_; |
|
|
|
|
|
lex.is_keyword = false; |
|
|
|
|
|
lex.is_operator = true; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "and"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::and_; |
|
|
|
|
|
lex.is_keyword = false; |
|
|
|
|
|
lex.is_operator = true; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "or"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::or_; |
|
|
|
|
|
lex.is_keyword = false; |
|
|
|
|
|
lex.is_operator = true; |
|
|
|
|
|
} |
|
|
|
|
|
else if(symbol_buffer == "xor"){ |
|
|
|
|
|
lex.type = gr_lexeme_type::xor_; |
|
|
|
|
|
lex.is_keyword = false; |
|
|
|
|
|
lex.is_operator = true; |
|
|
|
|
|
} else { |
|
|
|
|
|
lex.is_keyword = false; |
|
|
|
|
|
lex.type = gr_lexeme_type::identifier; |
|
|
|
|
|
lex.svalue = symbol_buffer; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
_lexemes.push_back(lex); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline string gr_lexer::convert_path_to_import(string& path) { |
|
|
|
|
|
return path; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline void gr_lexer::scan_file(gp::vector<char>& file_name){ |
|
|
|
|
|
_files_to_import.push_back(file_name); |
|
|
|
|
|
|
|
|
|
|
|
while (_files_to_import.size()) { |
|
|
|
|
|
_file = _files_to_import[_files_to_import.size()-1]; |
|
|
|
|
|
_files_imported.push_back(_file); |
|
|
|
|
|
_text = file_loader(file_name); |
|
|
|
|
|
_files_to_import.pop_back(); |
|
|
|
|
|
|
|
|
|
|
|
_line = 0u; |
|
|
|
|
|
_current = 0u; |
|
|
|
|
|
_lines = gp::vector<string>(_allocator); |
|
|
|
|
|
|
|
|
|
|
|
gp::vector<char> tmp(_allocator); |
|
|
|
|
|
for(char c : _text) { |
|
|
|
|
|
if(c == '\n') { |
|
|
|
|
|
_lines.push_back(tmp); |
|
|
|
|
|
tmp = gp::vector<char>(_allocator); |
|
|
|
|
|
} else { |
|
|
|
|
|
tmp.push_back(c); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
scan_script(); |
|
|
|
|
|
|
|
|
|
|
|
_file_id++; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline void gr_lexer::scan_file_path(){ |
|
|
|
|
|
gp_config::assertion(get() != '\"', "Expected \'\"\' at the beginning of the import."); |
|
|
|
|
|
_current++; |
|
|
|
|
|
|
|
|
|
|
|
string buffer(_allocator); |
|
|
|
|
|
for (;;) { |
|
|
|
|
|
gp_config::assertion(_current >= _text.size(), "Missing \'\"\' character."); |
|
|
|
|
|
char symbol = get(); |
|
|
|
|
|
if (symbol == '\n') { |
|
|
|
|
|
_position_of_line = _current; |
|
|
|
|
|
_line++; |
|
|
|
|
|
} |
|
|
|
|
|
else if (symbol == '\"') |
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
buffer.push_back(symbol); |
|
|
|
|
|
_current++; |
|
|
|
|
|
} |
|
|
|
|
|
buffer = convert_path_to_import(buffer); |
|
|
|
|
|
|
|
|
|
|
|
for(auto& file : _files_imported) { |
|
|
|
|
|
if(file == buffer) return; |
|
|
|
|
|
} |
|
|
|
|
|
for(auto& file : _files_to_import) { |
|
|
|
|
|
if(file == buffer) return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
_files_to_import.push_back(buffer); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
inline void gr_lexer::scan_use(){ |
|
|
|
|
|
advance(); |
|
|
|
|
|
|
|
|
|
|
|
// Multiple files import.
|
|
|
|
|
|
if (get() == '{') { |
|
|
|
|
|
advance(); |
|
|
|
|
|
bool isFirst = true; |
|
|
|
|
|
for (;;) { |
|
|
|
|
|
if (isFirst) |
|
|
|
|
|
isFirst = false; |
|
|
|
|
|
else if (get() == '\"') |
|
|
|
|
|
advance(); |
|
|
|
|
|
else |
|
|
|
|
|
gp_config::assertion(false, "Missing \'}\' after import list."); |
|
|
|
|
|
// EOF
|
|
|
|
|
|
gp_config::assertion(_current >= _text.size(), "Missing \'}\' after import list."); |
|
|
|
|
|
// End of the import list.
|
|
|
|
|
|
if (get() == '}') |
|
|
|
|
|
break; |
|
|
|
|
|
// Scan
|
|
|
|
|
|
scan_file_path(); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
else { |
|
|
|
|
|
scan_file_path(); |
|
|
|
|
|
} |
|
|
|
|
|
} |