add multi-line error report and span

This commit is contained in:
ValKmjolnir 2023-03-08 01:12:01 +08:00
parent abe2464a67
commit 1678567c5d
9 changed files with 862 additions and 783 deletions

View File

@ -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.

View File

@ -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运行环境无关的有趣的程序并用这个解释器来执行。你也可以让解释器来调用你自己编写的模块使它成为项目中一个非常有用的工具。

View File

@ -115,10 +115,13 @@ void execute(const string& file,const std::vector<string>& argv,const u32 cmd) {
}
i32 main(i32 argc,const char* argv[]) {
// output version info
if (argc<=1) {
std::clog<<logo;
return 0;
}
// run directly or show help
if (argc==2) {
string s(argv[1]);
if (s=="-h" || s=="--help") {
@ -130,6 +133,8 @@ i32 main(i32 argc,const char* argv[]) {
}
return 0;
}
// execute with arguments
std::unordered_map<string,u32> cmdlst={
{"--ast",VM_AST},{"-a",VM_AST},
{"--code",VM_CODE},{"-c",VM_CODE},

View File

@ -4,6 +4,7 @@
#include <cstring>
#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<ast> 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<ast>& child() const {return nd_child;}
std::vector<ast>& 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<string>& indent) const{
// output the indentation first
for(auto& i:indent) {
std::cout<<i;
}
// output ast node name
std::cout<<ast_name[nd_type];
// output string literal and number
if (nd_type==ast_str ||
nd_type==ast_id ||
nd_type==ast_bool ||
@ -212,7 +230,11 @@ void ast::print(u32 depth,bool last,std::vector<string>& indent) const{
} else if (nd_type==ast_num || nd_type==ast_file) {
std::cout<<":"<<nd_num;
}
std::cout<<" --> "<<nd_file<<":"<<nd_line<<":"<<nd_col<<"\n";
// okay, we must know that begin_column starts from index 0
std::cout<<" --> "<<loc.file<<":"<<loc.begin_line<<":"<<loc.begin_column+1<<"\n";
// output tree structure
if (last && depth) {
indent.back()=" ";
} else if (!last && depth) {

View File

@ -295,7 +295,7 @@ bool codegen::check_memory_reachable(const ast& node) {
void codegen::die(const string& info,const u32 line,const u32 col,const u32 len=1) {
err.load(file[fileindex]);
err.err("code",line,col,len,info);
err.err("code",{line,col-len,line,col,file[fileindex]},info);
}
void codegen::regist_num(const f64 num) {

View File

@ -1,160 +1,223 @@
#pragma once
#include <iostream>
#include <fstream>
#include <sstream> // MSVC need this to use std::getline
#include <cstring>
#include <vector>
#include "nasal.h"
#ifdef _WIN32
#include <windows.h> // 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<string> 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<<red<<"src: "<<reset<<"cannot open <"<<f<<">\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<<red<<stage<<": "<<white<<info<<reset<<"\n";
if (file.length()) {
std::cerr<<cyan<<" --> "<<red<<file<<"\n\n";
} else {
std::cerr<<"\n";
}
std::exit(1);
}
void err(const string& stage,const string& info) {
++cnt;
std::cerr<<red<<stage<<": "<<white<<info<<reset<<"\n";
if (file.length()) {
std::cerr<<cyan<<" --> "<<red<<file<<"\n\n";
} else {
std::cerr<<"\n";
}
}
void err(const string& stage,u32 line,u32 col,u32 len,const string& info) {
++cnt;
col=col?col:1;
len=len?len:1;
std::cerr
<<red<<stage<<": "<<white<<info<<reset<<"\n"
<<cyan<<" --> "<<red<<file<<":"<<line<<":"<<col<<reset<<"\n";
const string& code=line?res[line-1]:"# empty line";
const string iden=identation(std::to_string(line).length());
std::cerr
<<cyan<<line<<" | "<<reset<<code<<"\n"
<<cyan<<iden<<" | "<<reset;
for(i32 i=0;i<(i32)col-(i32)len;++i)
std::cerr<<char(" \t"[code[i]=='\t']);
for(u32 i=0;i<len;++i)
std::cerr<<red<<"^";
std::cerr<<red<<" "<<info<<reset<<"\n\n";
}
void chkerr() const {
if (cnt) {
std::exit(1);
}
}
};
#pragma once
#include <iostream>
#include <fstream>
#include <sstream> // MSVC need this to use std::getline
#include <cstring>
#include <vector>
#include "nasal.h"
struct span {
u32 begin_line;
u32 begin_column;
u32 end_line;
u32 end_column;
string file;
};
#ifdef _WIN32
#include <windows.h> // 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<string> 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()<len) {
res=" "+res;
}
return res;
}
public:
error():cnt(0) {}
void fatal(const string&,const string&);
void err(const string&,const string&);
void err(const string&,const span&,const string&);
void chkerr() const {
if (cnt) {
std::exit(1);
}
}
};
void flstream::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<<red<<"src: "<<reset<<"cannot open <"<<f<<">\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<<red<<stage<<": "<<white<<info<<reset<<"\n";
if (file.length()) {
std::cerr<<cyan<<" --> "<<red<<file<<"\n\n";
} else {
std::cerr<<"\n";
}
std::exit(1);
}
void error::err(const string& stage,const string& info) {
++cnt;
std::cerr<<red<<stage<<": "<<white<<info<<reset<<"\n";
if (file.length()) {
std::cerr<<cyan<<" --> "<<red<<file<<"\n\n";
} else {
std::cerr<<"\n";
}
}
void error::err(const string& stage,const span& loc,const string& info) {
// load error occurred file into string lines
load(loc.file);
++cnt;
std::cerr
<<red<<stage<<": "<<white<<info<<reset<<"\n"<<cyan<<" --> "
<<red<<loc.file<<":"<<loc.begin_line<<":"<<loc.begin_column+1<<reset<<"\n";
const usize maxlen=std::to_string(loc.end_line).length();
const string iden=identation(maxlen);
for(u32 line=loc.begin_line;line<=loc.end_line;++line) {
if (!line || !res[line-1].length()) {
continue;
}
if (loc.begin_line<line && line<loc.end_line) {
if (line==loc.begin_line+1) {
std::cerr<<cyan<<iden<<" | "<<reset<<"...\n"<<cyan<<iden<<" | "<<reset<<"\n";
}
continue;
}
const string& code=res[line-1];
std::cerr<<cyan<<leftpad(line,maxlen)<<" | "<<reset<<code<<"\n";
// output underline
std::cerr<<cyan<<iden<<" | "<<reset;
if (loc.begin_line==loc.end_line) {
for(i32 i=0;i<loc.begin_column;++i) {
std::cerr<<char(" \t"[code[i]=='\t']);
}
for(i32 i=loc.begin_column;i<loc.end_column;++i) {
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^")<<reset;
}
} else if (line==loc.begin_line) {
for(i32 i=0;i<loc.begin_column;++i) {
std::cerr<<char(" \t"[code[i]=='\t']);
}
for(i32 i=loc.begin_column;i<code.size();++i) {
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^")<<reset;
}
} else if (loc.begin_line<line && line<loc.end_line) {
for(i32 i=0;i<code.size();++i) {
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^");
}
} else {
for(i32 i=0;i<loc.end_column;++i) {
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^");
}
}
if (line==loc.end_line) {
std::cerr<<reset;
} else {
std::cerr<<reset<<"\n";
}
}
std::cerr<<"\n\n";
}

View File

@ -159,7 +159,7 @@ ast linker::fimpt(ast& node) {
// avoid infinite loading loop
filename=findf(filename);
if (!filename.length() || exist(filename)) {
return {0,0,ast_root,filename};
return {{0,0,0,0,filename},ast_root};
}
// start importing...
@ -176,12 +176,12 @@ ast linker::libimpt() {
parse par(err);
string filename=findf("lib.nas");
if (!filename.length()) {
return {0,0,ast_root,filename};
return {{0,0,0,0,filename},ast_root};
}
// avoid infinite loading loop
if (exist(filename)) {
return {0,0,ast_root,filename};
return {{0,0,0,0,filename},ast_root};
}
// start importing...
@ -194,7 +194,7 @@ ast linker::libimpt() {
}
ast linker::load(ast& root,u16 fileindex) {
ast tree(0,0,ast_root,files[fileindex]);
ast tree({0,0,0,0,files[fileindex]},ast_root);
if (!lib_loaded) {
link(tree,libimpt());
lib_loaded=true;
@ -207,7 +207,7 @@ ast linker::load(ast& root,u16 fileindex) {
}
}
// add root to the back of tree
ast file_head(0,0,ast_file,files[fileindex]);
ast file_head({0,0,0,0,files[fileindex]},ast_file);
file_head.set_num(fileindex);
tree.add(std::move(file_head));
link(tree,std::move(root));

File diff suppressed because it is too large Load Diff

View File

@ -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 ||