diff --git a/README.md b/README.md index 631b8bb..f71c581 100644 --- a/README.md +++ b/README.md @@ -29,25 +29,25 @@ __Contact us if having great ideas to share!__ ## __Introduction__ [Nasal](http://wiki.flightgear.org/Nasal_scripting_language) -is an ECMAscript-like language that used in [FlightGear](https://www.flightgear.org/). +is an ECMAscript-like language used in [FlightGear](https://www.flightgear.org/). The designer is [Andy Ross](https://github.com/andyross). This interpreter is totally rewritten by [ValKmjolnir](https://github.com/ValKmjolnir) using `C++`(`-std=c++14`) without reusing the code in [Andy Ross's nasal interpreter](https://github.com/andyross/nasal). -But we really appreciate that Andy created this amazing programming language and his interpreter. +But we really appreciate that Andy created this amazing programming language. This project uses __MIT license__ (2021/5/4). -__Why writing this nasal interpreter?__ -In 2019 summer holiday, +### __Why writing this nasal interpreter?__ + +2019 summer, members in [FGPRC](https://www.fgprc.org/) told me that it is hard to debug with nasal-console in Flightgear, especially when checking syntax errors. -So i wrote a new interpreter to help them checking syntax error and even, runtime error. +So i wrote a new interpreter to help checking syntax error and runtime error. I wrote the lexer, parser and bytecode virtual machine to help checking errors. -We found it much easier to check syntax and runtime -errors. +We found it much easier to debug. You could also use this language to write some interesting programs and run them without the lib of Flightgear. diff --git a/doc/README_zh.md b/doc/README_zh.md index 3a9cbd0..6bf3be7 100644 --- a/doc/README_zh.md +++ b/doc/README_zh.md @@ -29,17 +29,18 @@ __如果有好的意见或建议,欢迎联系我们!__ ## __简介__ [Nasal](http://wiki.flightgear.org/Nasal_scripting_language) -是一个与ECMAscript标准语法设计相似的编程语言,并且作为运行脚本被著名的开源飞行模拟器 [FlightGear](https://www.flightgear.org/) 所依赖。 +是一款语法与ECMAscript相似的编程语言,并作为运行脚本被著名开源飞行模拟器 [FlightGear](https://www.flightgear.org/) 所使用。 该语言的设计者为 [Andy Ross](https://github.com/andyross)。 -该解释器项目由 [ValKmjolnir](https://github.com/ValKmjolnir) 完全使用 `C++`(`-std=c++14`)重新实现,没有复用 [Andy Ross的nasal解释器](https://github.com/andyross/nasal) 中的任何一行代码。尽管没有参考任何代码,我们依然非常感谢Andy为我们带来了这样一个神奇且容易上手的编程语言。 +该解释器项目由 [ValKmjolnir](https://github.com/ValKmjolnir) 完全使用 `C++`(`-std=c++14`)重新实现,没有复用 [Andy Ross的nasal解释器](https://github.com/andyross/nasal) 中的任何一行代码。尽管没有参考任何代码,我们依然非常感谢Andy为我们带来了这样一个神奇且简洁的编程语言。 该项目已使用 __MIT__ 协议开源 (2021/5/4)。 -__我们为什么想要重新写一个nasal解释器?__ -2019年暑假,[FGPRC](https://www.fgprc.org.cn/) 的成员告诉我,在Flightgear中提供的nasal控制台窗口中进行调试很不方便,仅仅是想检查语法错误,也得花时间打开软件等待加载进去后进行调试。所以我就写了一个全新的解释器来帮助他们检查语法错误,甚至是检查运行时的错误。 +### __我们为什么想要重新写一个nasal解释器?__ -我编写了nasal的词法分析器和语法分析器,以及一个全新的字节码虚拟机,并用这个运行时来进行nasal程序的调试。我们发现使用这个解释器来检测语法和运行时错误提高了我们的工作效率。 +2019年暑假,[FGPRC](https://www.fgprc.org.cn/) 的成员告诉我,在Flightgear中提供的nasal控制台窗口中进行调试很不方便,仅仅是想检查语法错误,也得花时间打开软件等待加载进去后进行调试。所以我就写了一个全新的解释器来帮助他们检查语法错误以及运行时错误。 + +我编写了nasal的词法分析器和语法分析器,以及一个全新的字节码虚拟机,并用这个运行时来进行nasal程序的调试。我们发现使用这个解释器来检测语法和运行时错误极大的提高了效率。 你也可以使用这个语言来写一些与Flightgear运行环境无关的有趣的程序,并用这个解释器来执行。你也可以让解释器来调用你自己编写的模块,使它成为项目中一个非常有用的工具。 diff --git a/main.cpp b/main.cpp index 74af311..0f3a85e 100644 --- a/main.cpp +++ b/main.cpp @@ -115,10 +115,13 @@ void execute(const string& file,const std::vector& argv,const u32 cmd) { } i32 main(i32 argc,const char* argv[]) { + // output version info if (argc<=1) { std::clog< cmdlst={ {"--ast",VM_AST},{"-a",VM_AST}, {"--code",VM_CODE},{"-c",VM_CODE}, diff --git a/nasal_ast.h b/nasal_ast.h index 8e0ce94..de11977 100644 --- a/nasal_ast.h +++ b/nasal_ast.h @@ -4,6 +4,7 @@ #include #include "nasal.h" +#include "nasal_err.h" enum ast_node:u32 { ast_null=0, // null node @@ -145,16 +146,15 @@ const char* ast_name[]={ class ast { private: - u32 nd_line; - u32 nd_col; + span loc; u32 nd_type; f64 nd_num; - string nd_file; string nd_str; std::vector nd_child; + public: - ast(const u32 l,const u32 c,const u32 t,const std::string& f) - : nd_line(l),nd_col(c),nd_type(t),nd_num(0),nd_file(f),nd_str("") {} + ast(const span& s,const u32 t) + : loc(s),nd_type(t),nd_num(0),nd_str("") {} ast(const ast&) = default; ast(ast&&) = default; ast& operator=(const ast&) = default; @@ -170,22 +170,35 @@ public: void add(ast&& node) {nd_child.push_back(std::move(node));} void add(const ast& node) {nd_child.push_back(node);} - void set_line(const u32 l) {nd_line=l;} + void set_begin(const u32,const u32); + void set_end(const u32,const u32); void set_type(const u32 t) {nd_type=t;} void set_str(const string& s) {nd_str=s;} void set_num(const f64 n) {nd_num=n;} - u32 line() const {return nd_line;} - u32 col() const {return nd_col;} + u32 line() const {return loc.end_line;} + u32 col() const {return loc.end_column;} u32 type() const {return nd_type;} f64 num() const {return nd_num;} const string& str() const {return nd_str;} + const string& file() const {return loc.file;} + const span& location() const {return loc;} const std::vector& child() const {return nd_child;} std::vector& child() {return nd_child;} }; +void ast::set_begin(const u32 l,const u32 c) { + loc.begin_line=l; + loc.begin_column=c; +} + +void ast::set_end(const u32 l,const u32 c) { + loc.end_line=l; + loc.end_column=c; +} + void ast::clear() { - nd_line=nd_col=0; + loc={0,0,0,0}; nd_num=0; nd_str.clear(); nd_type=ast_null; @@ -198,10 +211,15 @@ void ast::dump() const{ } void ast::print(u32 depth,bool last,std::vector& indent) const{ + // output the indentation first for(auto& i:indent) { std::cout<& indent) const{ } else if (nd_type==ast_num || nd_type==ast_file) { std::cout<<":"< "< "< -#include -#include // MSVC need this to use std::getline -#include -#include - -#include "nasal.h" - -#ifdef _WIN32 -#include // use SetConsoleTextAttribute -struct for_reset { - CONSOLE_SCREEN_BUFFER_INFO scr; - for_reset() { - GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&scr); - } -} reset_ter_color; -#endif - -std::ostream& back_white(std::ostream& s) { -#ifdef _WIN32 - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0xf0); -#else - s<<"\033[7m"; -#endif - return s; -} - -std::ostream& red(std::ostream& s) { -#ifdef _WIN32 - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x0c); -#else - s<<"\033[91;1m"; -#endif - return s; -} - -std::ostream& cyan(std::ostream& s) { -#ifdef _WIN32 - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x03); -#else - s<<"\033[36;1m"; -#endif - return s; -} - -std::ostream& orange(std::ostream& s) { -#ifdef _WIN32 - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x0e); -#else - s<<"\033[93;1m"; -#endif - return s; -} - -std::ostream& white(std::ostream& s) { -#ifdef _WIN32 - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x0f); -#else - s<<"\033[0m\033[1m"; -#endif - return s; -} - -std::ostream& reset(std::ostream& s) { -#ifdef _WIN32 - SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),reset_ter_color.scr.wAttributes); -#else - s<<"\033[0m"; -#endif - return s; -} - -class flstream { -protected: - string file; - std::vector res; -public: - flstream():file("") {} - void load(const string& f) { - if (file==f) { // don't need to load a loaded file - return; - } else { - file=f; - } - - res.clear(); - std::ifstream in(f,std::ios::binary); - if (in.fail()) { - std::cerr<\n"; - std::exit(1); - } - - while(!in.eof()) { - string line; - std::getline(in,line); - res.push_back(line); - } - } - const string& operator[](usize n) const {return res[n];} - const string& name() const {return file;} - usize size() const {return res.size();} -}; - -class error:public flstream { -private: - u32 cnt; - string identation(usize len) { - string tmp=""; - tmp.resize(len,' '); - return tmp; - } -public: - error():cnt(0) {} - void fatal(const string& stage,const string& info) { - std::cerr< "< "< "< +#include +#include // MSVC need this to use std::getline +#include +#include + +#include "nasal.h" + +struct span { + u32 begin_line; + u32 begin_column; + u32 end_line; + u32 end_column; + string file; +}; + +#ifdef _WIN32 +#include // use SetConsoleTextAttribute +struct for_reset { + CONSOLE_SCREEN_BUFFER_INFO scr; + for_reset() { + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&scr); + } +} reset_ter_color; +#endif + +std::ostream& back_white(std::ostream& s) { +#ifdef _WIN32 + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0xf0); +#else + s<<"\033[7m"; +#endif + return s; +} + +std::ostream& red(std::ostream& s) { +#ifdef _WIN32 + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x0c); +#else + s<<"\033[91;1m"; +#endif + return s; +} + +std::ostream& cyan(std::ostream& s) { +#ifdef _WIN32 + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x03); +#else + s<<"\033[36;1m"; +#endif + return s; +} + +std::ostream& orange(std::ostream& s) { +#ifdef _WIN32 + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x0e); +#else + s<<"\033[93;1m"; +#endif + return s; +} + +std::ostream& white(std::ostream& s) { +#ifdef _WIN32 + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),0x0f); +#else + s<<"\033[0m\033[1m"; +#endif + return s; +} + +std::ostream& reset(std::ostream& s) { +#ifdef _WIN32 + SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),reset_ter_color.scr.wAttributes); +#else + s<<"\033[0m"; +#endif + return s; +} + +class flstream { +protected: + string file; + std::vector res; +public: + flstream():file("") {} + void load(const string&); + const string& operator[](usize n) const {return res[n];} + const string& name() const {return file;} + usize size() const {return res.size();} +}; + +class error:public flstream { +private: + u32 cnt; // counter for errors + + string identation(usize len) { + return string(len,' '); + } + string leftpad(u32 num,usize len) { + string res=std::to_string(num); + while(res.length()\n"; + std::exit(1); + } + + while(!in.eof()) { + string line; + std::getline(in,line); + res.push_back(line); + } +} + +void error::fatal(const string& stage,const string& info) { + std::cerr< "< "< " + < -#include -#include -#include -#include - -#include "nasal.h" -#include "nasal_err.h" - -#ifdef _MSC_VER -#define S_ISREG(m) (((m)&0xF000)==0x8000) -#endif - -enum class tok:u32 { - null=0, // null token (default token type) - num, // number literal - str, // string literal - id, // identifier - tktrue, // keyword true - tkfalse, // keyword false - rfor, // loop keyword for - forindex, // loop keyword forindex - foreach, // loop keyword foreach - rwhile, // loop keyword while - var, // keyword for definition - func, // keyword for definition of function - brk, // loop keyword break - cont, // loop keyword continue - ret, // function keyword return - rif, // condition expression keyword if - elsif, // condition expression keyword elsif - relse, // condition expression keyword else - tknil, // nil literal - lcurve, // ( - rcurve, // ) - lbracket, // [ - rbracket, // ] - lbrace, // { - rbrace, // } - semi, // ; - opand, // operator and - opor, // operator or - comma, // , - dot, // . - ellipsis, // ... - quesmark, // ? - colon, // : - add, // operator + - sub, // operator - - mult, // operator * - div, // operator / - floater, // operator ~ and binary operator ~ - btand, // bitwise operator & - btor, // bitwise operator | - btxor, // bitwise operator ^ - opnot, // operator ! - eq, // operator = - addeq, // operator += - subeq, // operator -= - multeq, // operator *= - diveq, // operator /= - lnkeq, // operator ~= - btandeq, // operator &= - btoreq, // operator |= - btxoreq, // operator ^= - cmpeq, // operator == - neq, // operator != - less, // operator < - leq, // operator <= - grt, // operator > - geq, // operator >= - eof // end of token list -}; - -struct token { - u32 tk_begin_line; // token begin line - u32 tk_begin_column;// token begin column - u32 tk_end_line; // token end line - u32 tk_end_column; // token end column - tok type; // token type - string str; // content - string file; // scanned file path - token() = default; - token(const token&) = default; -}; - -class lexer { -private: - u32 line; - u32 column; - usize ptr; - string filename; - string res; - error& err; - std::vector toks; - std::unordered_map typetbl { - {"true" ,tok::tktrue }, - {"false" ,tok::tkfalse }, - {"for" ,tok::rfor }, - {"forindex",tok::forindex}, - {"foreach" ,tok::foreach }, - {"while" ,tok::rwhile }, - {"var" ,tok::var }, - {"func" ,tok::func }, - {"break" ,tok::brk }, - {"continue",tok::cont }, - {"return" ,tok::ret }, - {"if" ,tok::rif }, - {"elsif" ,tok::elsif }, - {"else" ,tok::relse }, - {"nil" ,tok::tknil }, - {"(" ,tok::lcurve }, - {")" ,tok::rcurve }, - {"[" ,tok::lbracket}, - {"]" ,tok::rbracket}, - {"{" ,tok::lbrace }, - {"}" ,tok::rbrace }, - {";" ,tok::semi }, - {"and" ,tok::opand }, - {"or" ,tok::opor }, - {"," ,tok::comma }, - {"." ,tok::dot }, - {"..." ,tok::ellipsis}, - {"?" ,tok::quesmark}, - {":" ,tok::colon }, - {"+" ,tok::add }, - {"-" ,tok::sub }, - {"*" ,tok::mult }, - {"/" ,tok::div }, - {"~" ,tok::floater }, - {"&" ,tok::btand }, - {"|" ,tok::btor }, - {"^" ,tok::btxor }, - {"!" ,tok::opnot }, - {"=" ,tok::eq }, - {"+=" ,tok::addeq }, - {"-=" ,tok::subeq }, - {"*=" ,tok::multeq }, - {"/=" ,tok::diveq }, - {"~=" ,tok::lnkeq }, - {"&=" ,tok::btandeq }, - {"|=" ,tok::btoreq }, - {"^=" ,tok::btxoreq }, - {"==" ,tok::cmpeq }, - {"!=" ,tok::neq }, - {"<" ,tok::less }, - {"<=" ,tok::leq }, - {">" ,tok::grt }, - {">=" ,tok::geq } - }; - - tok get_type(const string&); - bool skip(char); - bool is_id(char); - bool is_hex(char); - bool is_oct(char); - bool is_dec(char); - bool is_str(char); - bool is_single_opr(char); - bool is_calc_opr(char); - - void skip_note(); - void err_char(); - - void open(const string&); - string utf8_gen(); - token id_gen(); - token num_gen(); - token str_gen(); - token single_opr(); - token dots(); - token calc_opr(); -public: - lexer(error& e): line(1),column(0),ptr(0),filename(""),res(""),err(e) {} - const error& scan(const string&); - const std::vector& result() const {return toks;} -}; - -bool lexer::skip(char c) { - return c==' '||c=='\n'||c=='\t'||c=='\r'||c==0; -} - -bool lexer::is_id(char c) { - return (c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z')||(c<0); -} - -bool lexer::is_hex(char c) { - return ('0'<=c&&c<='9')||('a'<=c&&c<='f')||('A'<=c && c<='F'); -} - -bool lexer::is_oct(char c) { - return '0'<=c&&c<='7'; -} - -bool lexer::is_dec(char c) { - return '0'<=c&&c<='9'; -} - -bool lexer::is_str(char c) { - return c=='\''||c=='\"'||c=='`'; -} - -bool lexer::is_single_opr(char c) { - return ( - c=='('||c==')'||c=='['||c==']'|| - c=='{'||c=='}'||c==','||c==';'|| - c==':'||c=='?'||c=='`'||c=='@'|| - c=='%'||c=='$'||c=='\\' - ); -} - -bool lexer::is_calc_opr(char c) { - return ( - c=='='||c=='+'||c=='-'||c=='*'|| - c=='!'||c=='/'||c=='<'||c=='>'|| - c=='~'||c=='|'||c=='&'||c=='^' - ); -} - -void lexer::skip_note() { - // avoid note, after this process ptr will point to a '\n', so next loop line counter+1 - while(++ptr is not a regular file"); - err.chkerr(); - } - - // load - filename=file; - std::ifstream in(file,std::ios::binary); - if (in.fail()) { - err.err("lexer","failed to open <"+file+">"); - } else { - err.load(file); - } - std::stringstream ss; - ss<"); - err.fatal("lexer","fatal error occurred, stop"); - } - str+=tmp; - column+=2; // may have some problems because not all the unicode takes 2 space - } - return str; -} - -token lexer::id_gen() { - u32 begin_line=line; - u32 begin_column=column; - string str=""; - while(ptr [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*) - string str=""; - while(ptr=res.size()) { - err.err("lexer",line,column,1,"get EOF when generating string"); - return {begin_line,begin_column,line,column,tok::str,str,filename}; - } - ++column; - if (begin=='`' && str.length()!=1) { - err.err("lexer",line,column,1,"\'`\' is used for string that includes one character"); - } - return {begin_line,begin_column,line,column,tok::str,str,filename}; -} - -token lexer::single_opr() { - u32 begin_line=line; - u32 begin_column=column; - string str(1,res[ptr]); - ++column; - tok type=get_type(str); - if (type==tok::null) { - err.err("lexer",line,column,str.length(),"invalid operator `"+str+"`"); - } - ++ptr; - return {begin_line,begin_column,line,column,type,str,filename}; -} - -token lexer::dots() { - u32 begin_line=line; - u32 begin_column=column; - string str="."; - if (ptr+2=res.size()) { - break; - } - if (is_id(res[ptr])) { - toks.push_back(id_gen()); - } else if (is_dec(res[ptr])) { - toks.push_back(num_gen()); - } else if (is_str(res[ptr])) { - toks.push_back(str_gen()); - } else if (is_single_opr(res[ptr])) { - toks.push_back(single_opr()); - } else if (res[ptr]=='.') { - toks.push_back(dots()); - } else if (is_calc_opr(res[ptr])) { - toks.push_back(calc_opr()); - } else if (res[ptr]=='#') { - skip_note(); - } else { - err_char(); - } - } - toks.push_back({line,column,line,column,tok::eof,"",filename}); - res=""; - return err; -} +#pragma once + +#include +#include +#include +#include +#include + +#include "nasal.h" +#include "nasal_err.h" + +#ifdef _MSC_VER +#define S_ISREG(m) (((m)&0xF000)==0x8000) +#endif + +enum class tok:u32 { + null=0, // null token (default token type) + num, // number literal + str, // string literal + id, // identifier + tktrue, // keyword true + tkfalse, // keyword false + rfor, // loop keyword for + forindex, // loop keyword forindex + foreach, // loop keyword foreach + rwhile, // loop keyword while + var, // keyword for definition + func, // keyword for definition of function + brk, // loop keyword break + cont, // loop keyword continue + ret, // function keyword return + rif, // condition expression keyword if + elsif, // condition expression keyword elsif + relse, // condition expression keyword else + tknil, // nil literal + lcurve, // ( + rcurve, // ) + lbracket, // [ + rbracket, // ] + lbrace, // { + rbrace, // } + semi, // ; + opand, // operator and + opor, // operator or + comma, // , + dot, // . + ellipsis, // ... + quesmark, // ? + colon, // : + add, // operator + + sub, // operator - + mult, // operator * + div, // operator / + floater, // operator ~ and binary operator ~ + btand, // bitwise operator & + btor, // bitwise operator | + btxor, // bitwise operator ^ + opnot, // operator ! + eq, // operator = + addeq, // operator += + subeq, // operator -= + multeq, // operator *= + diveq, // operator /= + lnkeq, // operator ~= + btandeq, // operator &= + btoreq, // operator |= + btxoreq, // operator ^= + cmpeq, // operator == + neq, // operator != + less, // operator < + leq, // operator <= + grt, // operator > + geq, // operator >= + eof // end of token list +}; + +struct token { + span loc; // location + tok type; // token type + string str; // content + token() = default; + token(const token&) = default; +}; + +class lexer { +private: + u32 line; + u32 column; + usize ptr; + string filename; + string res; + error& err; + std::vector toks; + std::unordered_map typetbl { + {"true" ,tok::tktrue }, + {"false" ,tok::tkfalse }, + {"for" ,tok::rfor }, + {"forindex",tok::forindex}, + {"foreach" ,tok::foreach }, + {"while" ,tok::rwhile }, + {"var" ,tok::var }, + {"func" ,tok::func }, + {"break" ,tok::brk }, + {"continue",tok::cont }, + {"return" ,tok::ret }, + {"if" ,tok::rif }, + {"elsif" ,tok::elsif }, + {"else" ,tok::relse }, + {"nil" ,tok::tknil }, + {"(" ,tok::lcurve }, + {")" ,tok::rcurve }, + {"[" ,tok::lbracket}, + {"]" ,tok::rbracket}, + {"{" ,tok::lbrace }, + {"}" ,tok::rbrace }, + {";" ,tok::semi }, + {"and" ,tok::opand }, + {"or" ,tok::opor }, + {"," ,tok::comma }, + {"." ,tok::dot }, + {"..." ,tok::ellipsis}, + {"?" ,tok::quesmark}, + {":" ,tok::colon }, + {"+" ,tok::add }, + {"-" ,tok::sub }, + {"*" ,tok::mult }, + {"/" ,tok::div }, + {"~" ,tok::floater }, + {"&" ,tok::btand }, + {"|" ,tok::btor }, + {"^" ,tok::btxor }, + {"!" ,tok::opnot }, + {"=" ,tok::eq }, + {"+=" ,tok::addeq }, + {"-=" ,tok::subeq }, + {"*=" ,tok::multeq }, + {"/=" ,tok::diveq }, + {"~=" ,tok::lnkeq }, + {"&=" ,tok::btandeq }, + {"|=" ,tok::btoreq }, + {"^=" ,tok::btxoreq }, + {"==" ,tok::cmpeq }, + {"!=" ,tok::neq }, + {"<" ,tok::less }, + {"<=" ,tok::leq }, + {">" ,tok::grt }, + {">=" ,tok::geq } + }; + + tok get_type(const string&); + bool skip(char); + bool is_id(char); + bool is_hex(char); + bool is_oct(char); + bool is_dec(char); + bool is_str(char); + bool is_single_opr(char); + bool is_calc_opr(char); + + void skip_note(); + void err_char(); + + void open(const string&); + string utf8_gen(); + token id_gen(); + token num_gen(); + token str_gen(); + token single_opr(); + token dots(); + token calc_opr(); +public: + lexer(error& e): line(1),column(0),ptr(0),filename(""),res(""),err(e) {} + const error& scan(const string&); + const std::vector& result() const {return toks;} +}; + +bool lexer::skip(char c) { + return c==' '||c=='\n'||c=='\t'||c=='\r'||c==0; +} + +bool lexer::is_id(char c) { + return (c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z')||(c<0); +} + +bool lexer::is_hex(char c) { + return ('0'<=c&&c<='9')||('a'<=c&&c<='f')||('A'<=c && c<='F'); +} + +bool lexer::is_oct(char c) { + return '0'<=c&&c<='7'; +} + +bool lexer::is_dec(char c) { + return '0'<=c&&c<='9'; +} + +bool lexer::is_str(char c) { + return c=='\''||c=='\"'||c=='`'; +} + +bool lexer::is_single_opr(char c) { + return ( + c=='('||c==')'||c=='['||c==']'|| + c=='{'||c=='}'||c==','||c==';'|| + c==':'||c=='?'||c=='`'||c=='@'|| + c=='%'||c=='$'||c=='\\' + ); +} + +bool lexer::is_calc_opr(char c) { + return ( + c=='='||c=='+'||c=='-'||c=='*'|| + c=='!'||c=='/'||c=='<'||c=='>'|| + c=='~'||c=='|'||c=='&'||c=='^' + ); +} + +void lexer::skip_note() { + // avoid note, after this process ptr will point to a '\n', so next loop line counter+1 + while(++ptr is not a regular file"); + err.chkerr(); + } + + // load + filename=file; + std::ifstream in(file,std::ios::binary); + if (in.fail()) { + err.err("lexer","failed to open <"+file+">"); + } else { + err.load(file); + } + std::stringstream ss; + ss<"); + err.fatal("lexer","fatal error occurred, stop"); + } + str+=tmp; + column+=2; // may have some problems because not all the unicode takes 2 space + } + return str; +} + +token lexer::id_gen() { + u32 begin_line=line; + u32 begin_column=column; + string str=""; + while(ptr [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*) + string str=""; + while(ptr=res.size()) { + err.err("lexer",{begin_line,begin_column,line,column,filename},"get EOF when generating string"); + return {{begin_line,begin_column,line,column,filename},tok::str,str}; + } + ++column; + if (begin=='`' && str.length()!=1) { + err.err("lexer",{begin_line,begin_column,line,column,filename},"\'`\' is used for string including one character"); + } + return {{begin_line,begin_column,line,column,filename},tok::str,str}; +} + +token lexer::single_opr() { + u32 begin_line=line; + u32 begin_column=column; + string str(1,res[ptr]); + ++column; + tok type=get_type(str); + if (type==tok::null) { + err.err("lexer",{begin_line,begin_column,line,column,filename},"invalid operator `"+str+"`"); + } + ++ptr; + return {{begin_line,begin_column,line,column,filename},type,str}; +} + +token lexer::dots() { + u32 begin_line=line; + u32 begin_column=column; + string str="."; + if (ptr+2=res.size()) { + break; + } + if (is_id(res[ptr])) { + toks.push_back(id_gen()); + } else if (is_dec(res[ptr])) { + toks.push_back(num_gen()); + } else if (is_str(res[ptr])) { + toks.push_back(str_gen()); + } else if (is_single_opr(res[ptr])) { + toks.push_back(single_opr()); + } else if (res[ptr]=='.') { + toks.push_back(dots()); + } else if (is_calc_opr(res[ptr])) { + toks.push_back(calc_opr()); + } else if (res[ptr]=='#') { + skip_note(); + } else { + err_char(); + } + } + toks.push_back({{line,column,line,column,filename},tok::eof,""}); + res=""; + return err; +} diff --git a/nasal_parse.h b/nasal_parse.h index 44c86e7..511eb18 100644 --- a/nasal_parse.h +++ b/nasal_parse.h @@ -43,9 +43,8 @@ */ class parse { -#define thisline (toks[ptr].tk_end_line) -#define thiscol (toks[ptr].tk_end_column) -#define thislen (toks[ptr].str.length()) +#define thisspan (toks[ptr].loc) +#define prevspan (ptr!=0? toks[ptr-1].loc:toks[ptr].loc) private: u32 ptr; u32 in_func; // count function block @@ -107,7 +106,7 @@ private: {tok::geq ,">=" } }; - void die(u32,u32,u32,string,bool); + void die(const span&,string); void next() {++ptr;}; void match(tok type,const char* info=nullptr); bool lookahead(tok); @@ -165,7 +164,8 @@ private: public: parse(error& e): ptr(0),in_func(0),in_loop(0), - toks(nullptr),root(0,0,ast_root,""), + toks(nullptr), + root({0,0,0,0,""},ast_root), err(e) {} const error& compile(const lexer&); ast& tree() {return root;} @@ -176,46 +176,34 @@ const error& parse::compile(const lexer& lexer) { toks=lexer.result().data(); ptr=in_func=in_loop=0; - root={0,0,ast_root,toks[0].file}; + root={{0,0,0,0,toks[0].loc.file},ast_root}; while(!lookahead(tok::eof)) { root.add(expr()); if (lookahead(tok::semi)) { match(tok::semi); } else if (need_semi_check(root.child().back()) && !lookahead(tok::eof)) { // the last expression can be recognized without semi - die(thisline,thiscol,thislen,"expected \";\"",true); + die(prevspan,"expected \";\""); } } return err; } -void parse::die(u32 line,u32 col,u32 len,string info,bool prev=false) { - // tok::str's str has no \" - if (lookahead(tok::str)) { - col-=2; - len+=2; - } - // used to report lack of ',' ';' - if (prev && ptr) { - line=toks[ptr-1].tk_end_line; - col=toks[ptr-1].tk_end_column; - len=toks[ptr-1].str.length(); - len+=toks[ptr-1].type==tok::str?2:0; - } - err.err("parse",line,col,lookahead(tok::eof)?1:len,info); +void parse::die(const span& loc,string info) { + err.err("parse",loc,info); } void parse::match(tok type,const char* info) { if (!lookahead(type)) { if (info) { - die(thisline,thiscol,thislen,info); + die(thisspan,info); return; } switch(type) { - case tok::num:die(thisline,thiscol,thislen,"expected number"); break; - case tok::str:die(thisline,thiscol,thislen,"expected string"); break; - case tok::id: die(thisline,thiscol,thislen,"expected identifier");break; - default: die(thisline,thiscol,thislen,"expected '"+tokname[type]+"'"); break; + case tok::num:die(thisspan,"expected number"); break; + case tok::str:die(thisspan,"expected string"); break; + case tok::id: die(thisspan,"expected identifier");break; + default: die(thisspan,"expected '"+tokname[type]+"'"); break; } return; } @@ -236,7 +224,7 @@ bool parse::is_call(tok type) { bool parse::check_comma(const tok* panic_set) { for(u32 i=0;panic_set[i]!=tok::null;++i) { if (lookahead(panic_set[i])) { - die(thisline,thiscol,thislen,"expected ',' between scalars",true); + die(prevspan,"expected ',' between scalars"); return true; } } @@ -319,36 +307,36 @@ bool parse::need_semi_check(const ast& node) { } ast parse::null() { - return {toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_null,toks[ptr].file}; + return {toks[ptr].loc,ast_null}; } ast parse::nil() { - return {toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_nil,toks[ptr].file}; + return {toks[ptr].loc,ast_nil}; } ast parse::num() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_num,toks[ptr].file); + ast node(toks[ptr].loc,ast_num); node.set_num(str2num(toks[ptr].str.c_str())); match(tok::num); return node; } ast parse::str() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_str,toks[ptr].file); + ast node(toks[ptr].loc,ast_str); node.set_str(toks[ptr].str); match(tok::str); return node; } ast parse::id() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_id,toks[ptr].file); + ast node(toks[ptr].loc,ast_id); node.set_str(toks[ptr].str); match(tok::id); return node; } ast parse::bools() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_bool,toks[ptr].file); + ast node(toks[ptr].loc,ast_bool); node.set_str(toks[ptr].str); if (lookahead(tok::tktrue)) { match(tok::tktrue); @@ -368,7 +356,7 @@ ast parse::vec() { tok::func,tok::var,tok::lcurve,tok::floater, tok::lbrace,tok::lbracket,tok::null }; - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_vec,toks[ptr].file); + ast node(toks[ptr].loc,ast_vec); match(tok::lbracket); while(!lookahead(tok::rbracket)) { node.add(calc()); @@ -385,14 +373,14 @@ ast parse::vec() { } ast parse::hash() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_hash,toks[ptr].file); + ast node(toks[ptr].loc,ast_hash); match(tok::lbrace); while(!lookahead(tok::rbrace)) { node.add(pair()); if (lookahead(tok::comma)) { match(tok::comma); } else if (lookahead(tok::id) || lookahead(tok::str)) { // first set of hashmember - die(thisline,thiscol,thislen,"expected ',' between hash members",true); + die(prevspan,"expected ',' between hash members"); } else { break; } @@ -402,7 +390,7 @@ ast parse::hash() { } ast parse::pair() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_pair,toks[ptr].file); + ast node(toks[ptr].loc,ast_pair); if (lookahead(tok::id)) { node.add(id()); } else if (lookahead(tok::str)) { @@ -417,7 +405,7 @@ ast parse::pair() { ast parse::func() { ++in_func; - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_func,toks[ptr].file); + ast node(toks[ptr].loc,ast_func); match(tok::func); if (lookahead(tok::lcurve)) { node.add(params()); @@ -430,12 +418,12 @@ ast parse::func() { } ast parse::params() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_params,toks[ptr].file); + ast node(toks[ptr].loc,ast_params); match(tok::lcurve); while(!lookahead(tok::rcurve)) { ast tmp=id(); if (lookahead(tok::eq) || lookahead(tok::ellipsis)) { - ast special_arg(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_null,toks[ptr].file); + ast special_arg(toks[ptr].loc,ast_null); if (lookahead(tok::eq)) { match(tok::eq); special_arg=std::move(tmp); @@ -453,7 +441,7 @@ ast parse::params() { if (lookahead(tok::comma)) { match(tok::comma); } else if (lookahead(tok::id)) { // first set of identifier - die(thisline,thiscol,thislen,"expected ',' between identifiers",true); + die(prevspan,"expected ',' between identifiers"); } else { break; } @@ -471,10 +459,10 @@ ast parse::lcurve_expr() { ast parse::expr() { tok type=toks[ptr].type; if ((type==tok::brk || type==tok::cont) && !in_loop) { - die(thisline,thiscol,thislen,"must use break/continue in loops"); + die(thisspan,"must use break/continue in loops"); } if (type==tok::ret && !in_func) { - die(thisline,thiscol,thislen,"must use return in functions"); + die(thisspan,"must use return in functions"); } switch(type) { case tok::tknil: @@ -501,19 +489,19 @@ ast parse::expr() { case tok::ret: return ret_expr(); break; case tok::semi: break; default: - die(thisline,thiscol,thislen,"incorrect token <"+toks[ptr].str+">"); + die(thisspan,"incorrect token <"+toks[ptr].str+">"); next(); break; } - return {toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_null,toks[ptr].file}; + return {toks[ptr].loc,ast_null}; } ast parse::exprs() { if (lookahead(tok::eof)) { - die(thisline,thiscol,thislen,"expected expression block"); + die(thisspan,"expected expression block"); return null(); } - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_block,toks[ptr].file); + ast node(toks[ptr].loc,ast_block); if (lookahead(tok::lbrace)) { match(tok::lbrace); while(!lookahead(tok::rbrace) && !lookahead(tok::eof)) { @@ -522,7 +510,7 @@ ast parse::exprs() { match(tok::semi); } else if (need_semi_check(node.child().back()) && !lookahead(tok::rbrace)) { // the last expression can be recognized without semi - die(thisline,thiscol,thislen,"expected ';'",true); + die(prevspan,"expected ';'"); } } match(tok::rbrace,"expected '}' when generating expressions"); @@ -538,7 +526,7 @@ ast parse::calc() { ast node=bitwise_or(); if (lookahead(tok::quesmark)) { // trinocular calculation - ast tmp(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_trino,toks[ptr].file); + ast tmp(toks[ptr].loc,ast_trino); match(tok::quesmark); tmp.add(std::move(node)); tmp.add(calc()); @@ -547,13 +535,13 @@ ast parse::calc() { node=std::move(tmp); } else if (tok::eq<=toks[ptr].type && toks[ptr].type<=tok::lnkeq) { // tok::eq~tok::lnkeq is 37 to 42,ast_equal~ast_lnkeq is 21~26 - ast tmp(toks[ptr].tk_end_line,toks[ptr].tk_end_column,(u32)toks[ptr].type-(u32)tok::eq+ast_equal,toks[ptr].file); + ast tmp(toks[ptr].loc,(u32)toks[ptr].type-(u32)tok::eq+ast_equal); tmp.add(std::move(node)); match(toks[ptr].type); tmp.add(calc()); node=std::move(tmp); } else if (toks[ptr].type==tok::btandeq || toks[ptr].type==tok::btoreq || toks[ptr].type==tok::btxoreq) { - ast tmp(toks[ptr].tk_end_line,toks[ptr].tk_end_column,(u32)toks[ptr].type-(u32)tok::btandeq+ast_btandeq,toks[ptr].file); + ast tmp(toks[ptr].loc,(u32)toks[ptr].type-(u32)tok::btandeq+ast_btandeq); tmp.add(std::move(node)); match(toks[ptr].type); tmp.add(calc()); @@ -565,7 +553,7 @@ ast parse::calc() { ast parse::bitwise_or() { ast node=bitwise_xor(); while(lookahead(tok::btor)) { - ast tmp(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_bitor,toks[ptr].file); + ast tmp(toks[ptr].loc,ast_bitor); tmp.add(std::move(node)); match(tok::btor); tmp.add(bitwise_xor()); @@ -577,7 +565,7 @@ ast parse::bitwise_or() { ast parse::bitwise_xor() { ast node=bitwise_and(); while(lookahead(tok::btxor)) { - ast tmp(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_bitxor,toks[ptr].file); + ast tmp(toks[ptr].loc,ast_bitxor); tmp.add(std::move(node)); match(tok::btxor); tmp.add(bitwise_and()); @@ -589,7 +577,7 @@ ast parse::bitwise_xor() { ast parse::bitwise_and() { ast node=or_expr(); while(lookahead(tok::btand)) { - ast tmp(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_bitand,toks[ptr].file); + ast tmp(toks[ptr].loc,ast_bitand); tmp.add(std::move(node)); match(tok::btand); tmp.add(or_expr()); @@ -601,7 +589,7 @@ ast parse::bitwise_and() { ast parse::or_expr() { ast node=and_expr(); while(lookahead(tok::opor)) { - ast tmp(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_or,toks[ptr].file); + ast tmp(toks[ptr].loc,ast_or); tmp.add(std::move(node)); match(tok::opor); tmp.add(and_expr()); @@ -613,7 +601,7 @@ ast parse::or_expr() { ast parse::and_expr() { ast node=cmp_expr(); while(lookahead(tok::opand)) { - ast tmp(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_and,toks[ptr].file); + ast tmp(toks[ptr].loc,ast_and); tmp.add(std::move(node)); match(tok::opand); tmp.add(cmp_expr()); @@ -626,7 +614,7 @@ ast parse::cmp_expr() { ast node=additive_expr(); while(tok::cmpeq<=toks[ptr].type && toks[ptr].type<=tok::geq) { // tok::cmpeq~tok::geq is 43~48,ast_cmpeq~ast_geq is 27~32 - ast tmp(toks[ptr].tk_end_line,toks[ptr].tk_end_column,(u32)toks[ptr].type-(u32)tok::cmpeq+ast_cmpeq,toks[ptr].file); + ast tmp(toks[ptr].loc,(u32)toks[ptr].type-(u32)tok::cmpeq+ast_cmpeq); tmp.add(std::move(node)); match(toks[ptr].type); tmp.add(additive_expr()); @@ -638,7 +626,7 @@ ast parse::cmp_expr() { ast parse::additive_expr() { ast node=multive_expr(); while(lookahead(tok::add) || lookahead(tok::sub) || lookahead(tok::floater)) { - ast tmp(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_null,toks[ptr].file); + ast tmp(toks[ptr].loc,ast_null); switch(toks[ptr].type) { case tok::add: tmp.set_type(ast_add); break; case tok::sub: tmp.set_type(ast_sub); break; @@ -656,7 +644,7 @@ ast parse::additive_expr() { ast parse::multive_expr() { ast node=(lookahead(tok::sub) || lookahead(tok::opnot) || lookahead(tok::floater))?unary():scalar(); while(lookahead(tok::mult) || lookahead(tok::div)) { - ast tmp(toks[ptr].tk_end_line,toks[ptr].tk_end_column,(u32)toks[ptr].type-(u32)tok::mult+ast_mult,toks[ptr].file); + ast tmp(toks[ptr].loc,(u32)toks[ptr].type-(u32)tok::mult+ast_mult); tmp.add(std::move(node)); match(toks[ptr].type); tmp.add((lookahead(tok::sub) || lookahead(tok::opnot) || lookahead(tok::floater))?unary():scalar()); @@ -666,7 +654,7 @@ ast parse::multive_expr() { } ast parse::unary() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_null,toks[ptr].file); + ast node(toks[ptr].loc,ast_null); switch(toks[ptr].type) { case tok::sub: node.set_type(ast_neg);match(tok::sub);break; case tok::opnot: node.set_type(ast_lnot);match(tok::opnot);break; @@ -678,7 +666,7 @@ ast parse::unary() { } ast parse::scalar() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_null,toks[ptr].file); + ast node(toks[ptr].loc,ast_null); if (lookahead(tok::tknil)) { node=nil(); match(tok::tknil); @@ -707,13 +695,13 @@ ast parse::scalar() { match(tok::eq); node.add(calc()); } else { - die(thisline,thiscol,thislen,"expected scalar"); + die(thisspan,"expected scalar"); return node; } // check call and avoid ambiguous syntax if (is_call(toks[ptr].type) && !(lookahead(tok::lcurve) && toks[ptr+1].type==tok::var)) { ast tmp=std::move(node); - node={toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_call,toks[ptr].file}; + node={toks[ptr].loc,ast_call}; node.add(std::move(tmp)); while(is_call(toks[ptr].type)) { node.add(call_scalar()); @@ -730,11 +718,11 @@ ast parse::call_scalar() { default: break; } // should never run this expression - return {toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_nil,toks[ptr].file}; + return {toks[ptr].loc,ast_nil}; } ast parse::callh() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_callh,toks[ptr].file); + ast node(toks[ptr].loc,ast_callh); match(tok::dot); node.set_str(toks[ptr].str); match(tok::id,"expected hashmap key"); // get key @@ -751,7 +739,7 @@ ast parse::callv() { tok::func,tok::var,tok::lcurve,tok::floater, tok::lbrace,tok::lbracket,tok::colon,tok::null }; - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_callv,toks[ptr].file); + ast node(toks[ptr].loc,ast_callv); match(tok::lbracket); while(!lookahead(tok::rbracket)) { node.add(subvec()); @@ -764,7 +752,7 @@ ast parse::callv() { } } if (node.size()==0) { - die(node.line(),node.col(),1,"expected index value"); + die(node.location(),"expected index value"); } match(tok::rbracket,"expected ']' when calling vector"); return node; @@ -780,7 +768,7 @@ ast parse::callf() { tok::func,tok::var,tok::lcurve,tok::floater, tok::lbrace,tok::lbracket,tok::null }; - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_callf,toks[ptr].file); + ast node(toks[ptr].loc,ast_callf); bool special_call=check_special_call(); match(tok::lcurve); while(!lookahead(tok::rcurve)) { @@ -799,7 +787,7 @@ ast parse::callf() { ast parse::subvec() { ast node=lookahead(tok::colon)?nil():calc(); if (lookahead(tok::colon)) { - ast tmp(node.line(),node.col(),ast_subvec,toks[ptr].file); + ast tmp(toks[ptr].loc,ast_subvec); match(tok::colon); tmp.add(std::move(node)); tmp.add((lookahead(tok::comma) || lookahead(tok::rbracket))?nil():calc()); @@ -809,13 +797,13 @@ ast parse::subvec() { } ast parse::definition() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_def,toks[ptr].file); + ast node(toks[ptr].loc,ast_def); if (lookahead(tok::var)) { match(tok::var); switch(toks[ptr].type) { case tok::id: node.add(id());break; case tok::lcurve: node.add(outcurve_def());break; - default: die(thisline,thiscol,thislen,"expected identifier");break; + default: die(thisspan,"expected identifier");break; } } else if (lookahead(tok::lcurve)) { node.add(incurve_def()); @@ -845,17 +833,17 @@ ast parse::outcurve_def() { } ast parse::multi_id() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_multi_id,toks[ptr].file); + ast node(toks[ptr].loc,ast_multi_id); while(!lookahead(tok::eof)) { node.add(id()); if (is_call(toks[ptr].type)) { ast tmp=call_scalar();// recognize calls but this is still a syntax error - die(tmp.line(),tmp.col(),1,"cannot call identifier in multi-definition"); + die(tmp.location(),"cannot call identifier in multi-definition"); } if (lookahead(tok::comma)) { match(tok::comma); } else if (lookahead(tok::id)) { // first set of identifier - die(thisline,thiscol,thislen,"expected ',' between identifiers",true); + die(prevspan,"expected ',' between identifiers"); } else { break; } @@ -871,7 +859,7 @@ ast parse::multi_scalar() { tok::func,tok::var,tok::lcurve,tok::floater, tok::lbrace,tok::lbracket,tok::null }; - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_tuple,toks[ptr].file); + ast node(toks[ptr].loc,ast_tuple); match(tok::lcurve); while(!lookahead(tok::rcurve)) { node.add(calc()); @@ -888,11 +876,11 @@ ast parse::multi_scalar() { } ast parse::multi_assgin() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_multi_assign,toks[ptr].file); + ast node(toks[ptr].loc,ast_multi_assign); node.add(multi_scalar()); match(tok::eq); if (lookahead(tok::eof)) { - die(thisline,thiscol,thislen,"expected value list"); + die(thisspan,"expected value list"); return node; } if (lookahead(tok::lcurve)) { @@ -905,7 +893,7 @@ ast parse::multi_assgin() { ast parse::loop() { ++in_loop; - ast node(0,0,ast_null,toks[ptr].file); + ast node(toks[ptr].loc,ast_null); switch(toks[ptr].type) { case tok::rwhile: node=while_loop(); break; case tok::rfor: node=for_loop(); break; @@ -918,7 +906,7 @@ ast parse::loop() { } ast parse::while_loop() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_while,toks[ptr].file); + ast node(toks[ptr].loc,ast_while); match(tok::rwhile); match(tok::lcurve); node.add(calc()); @@ -928,12 +916,12 @@ ast parse::while_loop() { } ast parse::for_loop() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_for,toks[ptr].file); + ast node(toks[ptr].loc,ast_for); match(tok::rfor); match(tok::lcurve); // first expression if (lookahead(tok::eof)) { - die(thisline,thiscol,thislen,"expected definition"); + die(thisspan,"expected definition"); } if (lookahead(tok::semi)) { node.add(null()); @@ -947,7 +935,7 @@ ast parse::for_loop() { match(tok::semi,"expected ';' in for(;;)"); // conditional expression if (lookahead(tok::eof)) { - die(thisline,thiscol,thislen,"expected conditional expr"); + die(thisspan,"expected conditional expr"); } if (lookahead(tok::semi)) { node.add(null()); @@ -957,7 +945,7 @@ ast parse::for_loop() { match(tok::semi,"expected ';' in for(;;)"); //after loop expression if (lookahead(tok::eof)) { - die(thisline,thiscol,thislen,"expected calculation"); + die(thisspan,"expected calculation"); } if (lookahead(tok::rcurve)) { node.add(null()); @@ -970,7 +958,7 @@ ast parse::for_loop() { } ast parse::forei_loop() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_null,toks[ptr].file); + ast node(toks[ptr].loc,ast_null); switch(toks[ptr].type) { case tok::forindex:node.set_type(ast_forindex);match(tok::forindex);break; case tok::foreach: node.set_type(ast_foreach); match(tok::foreach); break; @@ -980,12 +968,12 @@ ast parse::forei_loop() { // first expression // foreach/forindex must have an iterator to loop through if (!lookahead(tok::var) && !lookahead(tok::id)) { - die(thisline,thiscol,thislen,"expected iterator"); + die(thisspan,"expected iterator"); } node.add(iter_gen()); match(tok::semi,"expected ';' in foreach/forindex(iter;vector)"); if (lookahead(tok::eof)) { - die(thisline,thiscol,thislen,"expected vector"); + die(thisspan,"expected vector"); } node.add(calc()); match(tok::rcurve); @@ -994,7 +982,7 @@ ast parse::forei_loop() { } ast parse::iter_gen() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_null,toks[ptr].file); + ast node(toks[ptr].loc,ast_null); if (lookahead(tok::var)) { match(tok::var); node.set_type(ast_iter); @@ -1010,8 +998,8 @@ ast parse::iter_gen() { } ast parse::cond() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_cond,toks[ptr].file); - ast ifnode(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_if,toks[ptr].file); + ast node(toks[ptr].loc,ast_cond); + ast ifnode(toks[ptr].loc,ast_if); match(tok::rif); match(tok::lcurve); ifnode.add(calc()); @@ -1019,7 +1007,7 @@ ast parse::cond() { ifnode.add(exprs()); node.add(std::move(ifnode)); while(lookahead(tok::elsif)) { - ast elsifnode(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_elsif,toks[ptr].file); + ast elsifnode(toks[ptr].loc,ast_elsif); match(tok::elsif); match(tok::lcurve); elsifnode.add(calc()); @@ -1028,7 +1016,7 @@ ast parse::cond() { node.add(std::move(elsifnode)); } if (lookahead(tok::relse)) { - ast elsenode(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_else,toks[ptr].file); + ast elsenode(toks[ptr].loc,ast_else); match(tok::relse); elsenode.add(exprs()); node.add(std::move(elsenode)); @@ -1037,19 +1025,19 @@ ast parse::cond() { } ast parse::continue_expr() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_continue,toks[ptr].file); + ast node(toks[ptr].loc,ast_continue); match(tok::cont); return node; } ast parse::break_expr() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_break,toks[ptr].file); + ast node(toks[ptr].loc,ast_break); match(tok::brk); return node; } ast parse::ret_expr() { - ast node(toks[ptr].tk_end_line,toks[ptr].tk_end_column,ast_ret,toks[ptr].file); + ast node(toks[ptr].loc,ast_ret); match(tok::ret); tok type=toks[ptr].type; if (type==tok::tknil || type==tok::num || type==tok::str || type==tok::id ||