Merge pull request #21 from ValKmjolnir/develop

👾 Update docs & optimize codes
This commit is contained in:
Li Haokun
2023-07-05 00:29:30 +08:00
committed by GitHub
19 changed files with 613 additions and 536 deletions

View File

@@ -1,4 +1,4 @@
# __Nasal Script__
# __Nasal - Modern Interpreter__
<img src="./doc/pic/header.png" style="width:800px"></img>
@@ -36,7 +36,7 @@ This interpreter is totally rewritten by [ValKmjolnir](https://github.com/ValKmj
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.
This project uses __MIT license__ (2019/7~2021/5/4~2023/5), __GPL v2 license__ (since 2023/6).
This project uses __MIT license__ (2019/7 ~ 2021/5/4 ~ 2023/5), __GPL v2 license__ (since 2023/6).
### __Why writing this nasal interpreter?__

View File

@@ -1,4 +1,4 @@
# __Nasal Script__
# __Nasal - Modern Interpreter__
<img src="../doc/pic/header.png" style="width:800px"></img>
@@ -32,9 +32,9 @@ __如果有好的意见或建议欢迎联系我们!__
是一款语法与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++17`)重新实现,没有复用 [Andy Ross的nasal解释器](https://github.com/andyross/nasal) 中的任何一行代码。尽管没有参考任何代码我们依然非常感谢Andy为我们带来了这样一个神奇且简洁的编程语言。
该项目使用 __MIT__ 协议开源 (2019/7~2021/5/4~2023/5),从 2023/6 开始使用 __GPL v2__ 协议。
该项目使用 __MIT__ 协议开源 (2019/7 ~ 2021/5/4 ~ 2023/5),从 2023/6 开始使用 __GPL v2__ 协议。
### __我们为什么想要重新写一个nasal解释器?__

View File

@@ -289,14 +289,29 @@ bool ast_dumper::visit_definition_expr(definition_expr* node) {
node->get_variables()->accept(this);
}
set_last();
node->get_value()->accept(this);
if (node->get_tuple()) {
node->get_tuple()->accept(this);
} else {
node->get_value()->accept(this);
}
pop_indent();
return true;
}
bool ast_dumper::visit_assignment_expr(assignment_expr* node) {
dump_indent();
std::cout << "assignment";
std::cout << "assignment ";
switch(node->get_assignment_type()) {
case assignment_expr::assign_type::add_equal: std::cout << "+="; break;
case assignment_expr::assign_type::sub_equal: std::cout << "-="; break;
case assignment_expr::assign_type::mult_equal: std::cout << "*="; break;
case assignment_expr::assign_type::div_equal: std::cout << "/="; break;
case assignment_expr::assign_type::concat_equal: std::cout << "~="; break;
case assignment_expr::assign_type::equal: std::cout << "="; break;
case assignment_expr::assign_type::bitwise_and_equal: std::cout << "&="; break;
case assignment_expr::assign_type::bitwise_or_equal: std::cout << "|="; break;
case assignment_expr::assign_type::bitwise_xor_equal: std::cout << "^="; break;
}
std::cout << format_location(node->get_location());
push_indent();
node->get_left()->accept(this);
@@ -308,7 +323,7 @@ bool ast_dumper::visit_assignment_expr(assignment_expr* node) {
bool ast_dumper::visit_multi_identifier(multi_identifier* node) {
dump_indent();
std::cout << "multiple_definition";
std::cout << "multiple_identifier";
std::cout << format_location(node->get_location());
push_indent();
for(auto i : node->get_variables()) {

View File

@@ -5,6 +5,11 @@ bool ast_visitor::visit_expr(expr* node) {
return true;
}
bool ast_visitor::visit_call(call* node) {
node->accept(this);
return true;
}
bool ast_visitor::visit_file_info(file_info* node) {
return true;
}
@@ -134,7 +139,11 @@ bool ast_visitor::visit_definition_expr(definition_expr* node) {
} else {
node->get_variables()->accept(this);
}
node->get_value()->accept(this);
if (node->get_tuple()) {
node->get_tuple()->accept(this);
} else {
node->get_value()->accept(this);
}
return true;
}

View File

@@ -5,6 +5,7 @@
class ast_visitor {
public:
virtual bool visit_expr(expr*);
virtual bool visit_call(call*);
virtual bool visit_file_info(file_info*);
virtual bool visit_null_expr(null_expr*);
virtual bool visit_nil_expr(nil_expr*);

View File

@@ -78,7 +78,7 @@ std::ostream& version(std::ostream& out) {
if (num<0.01) {
parse::easter_egg();
}
out << "version " << __nasver;
out << "nasal interpreter version " << __nasver;
out << " (" << __DATE__ << " " << __TIME__ << ")\n";
return out;
}
@@ -112,9 +112,8 @@ void execute(
// parser gets lexer's token list to compile
parse.compile(lex).chkerr();
if (cmd&VM_RAW_AST) {
auto dumper = new ast_dumper;
auto dumper = std::unique_ptr<ast_dumper>(new ast_dumper);
dumper->dump(parse.tree());
delete dumper;
}
// linker gets parser's ast and load import files to this ast
@@ -125,15 +124,14 @@ void execute(
opt->do_optimization(parse.tree());
delete opt;
if (cmd&VM_AST) {
auto dumper = new ast_dumper;
auto dumper = std::unique_ptr<ast_dumper>(new ast_dumper);
dumper->dump(parse.tree());
delete dumper;
}
// code generator gets parser's ast and import file list to generate code
gen.compile(parse, ld).chkerr();
if (cmd&VM_CODE) {
gen.print();
gen.print(std::cout);
}
// run

View File

@@ -5,6 +5,10 @@ void expr::accept(ast_visitor* visitor) {
visitor->visit_expr(this);
}
void call::accept(ast_visitor* visitor) {
visitor->visit_call(this);
}
void file_info::accept(ast_visitor* visitor) {
visitor->visit_file_info(this);
}
@@ -201,6 +205,9 @@ definition_expr::~definition_expr() {
if (variables) {
delete variables;
}
if (tuple) {
delete tuple;
}
if (value) {
delete value;
}

View File

@@ -51,6 +51,7 @@ class slice_vector;
class multi_identifier;
class code_block;
class if_expr;
class tuple_expr;
class expr {
protected:
@@ -75,6 +76,14 @@ public:
virtual void accept(ast_visitor*);
};
class call:public expr {
public:
call(const span& location, expr_type node_type):
expr(location, node_type) {}
~call() = default;
virtual void accept(ast_visitor*);
};
class file_info:public expr {
private:
uint16_t index;
@@ -352,7 +361,7 @@ public:
class call_expr:public expr {
private:
expr* first;
std::vector<expr*> calls;
std::vector<call*> calls;
public:
call_expr(const span& location):
@@ -360,45 +369,45 @@ public:
first(nullptr) {}
~call_expr();
void set_first(expr* node) {first = node;}
void add_call(expr* node) {calls.push_back(node);}
void add_call(call* node) {calls.push_back(node);}
expr* get_first() {return first;}
std::vector<expr*>& get_calls() {return calls;}
std::vector<call*>& get_calls() {return calls;}
void accept(ast_visitor*) override;
};
class call_hash:public expr {
class call_hash:public call {
private:
std::string field;
public:
call_hash(const span& location, const std::string& name):
expr(location, expr_type::ast_callh),
call(location, expr_type::ast_callh),
field(name) {}
~call_hash() = default;
const std::string& get_field() const {return field;}
void accept(ast_visitor*) override;
};
class call_vector:public expr {
class call_vector:public call {
private:
std::vector<slice_vector*> calls;
public:
call_vector(const span& location):
expr(location, expr_type::ast_callv) {}
call(location, expr_type::ast_callv) {}
~call_vector();
void add_slice(slice_vector* node) {calls.push_back(node);}
std::vector<slice_vector*>& get_slices() {return calls;}
void accept(ast_visitor*) override;
};
class call_function:public expr {
class call_function:public call {
private:
std::vector<expr*> args;
public:
call_function(const span& location):
expr(location, expr_type::ast_callf) {}
call(location, expr_type::ast_callf) {}
~call_function();
void add_argument(expr* node) {args.push_back(node);}
std::vector<expr*>& get_argument() {return args;}
@@ -426,18 +435,22 @@ class definition_expr:public expr {
private:
identifier* variable_name;
multi_identifier* variables;
tuple_expr* tuple;
expr* value;
public:
definition_expr(const span& location):
expr(location, expr_type::ast_def),
variable_name(nullptr), variables(nullptr), value(nullptr) {}
variable_name(nullptr), variables(nullptr),
tuple(nullptr), value(nullptr) {}
~definition_expr();
void set_identifier(identifier* node) {variable_name = node;}
void set_multi_define(multi_identifier* node) {variables = node;}
void set_tuple(tuple_expr* node) {tuple = node;}
void set_value(expr* node) {value = node;}
identifier* get_variable_name() {return variable_name;}
multi_identifier* get_variables() {return variables;}
tuple_expr* get_tuple() {return tuple;}
expr* get_value() {return value;}
void accept(ast_visitor*) override;
};
@@ -562,7 +575,7 @@ public:
class iter_expr:public expr {
private:
identifier* name;
expr* call;
call_expr* call;
public:
iter_expr(const span& location):
@@ -570,9 +583,9 @@ public:
name(nullptr), call(nullptr) {}
~iter_expr();
void set_name(identifier* node) {name = node;}
void set_call(expr* node) {call = node;}
void set_call(call_expr* node) {call = node;}
identifier* get_name() {return name;}
expr* get_call() {return call;}
call_expr* get_call() {return call;}
void accept(ast_visitor*) override;
};

View File

@@ -27,30 +27,31 @@ void codegen::check_id_exist(identifier* node) {
}
void codegen::regist_num(const f64 num) {
if (!num_table.count(num)) {
u32 size = num_table.size();
num_table[num] = size;
num_res.push_back(num);
if (const_number_map.count(num)) {
return;
}
u32 size = const_number_map.size();
const_number_map[num] = size;
const_number_table.push_back(num);
}
void codegen::regist_str(const std::string& str) {
if (!str_table.count(str)) {
u32 size = str_table.size();
str_table[str] = size;
str_res.push_back(str);
if (const_string_map.count(str)) {
return;
}
u32 size = const_string_map.size();
const_string_map[str] = size;
const_string_table.push_back(str);
}
void codegen::find_symbol(code_block* node) {
auto finder = new symbol_finder;
auto finder = std::unique_ptr<symbol_finder>(new symbol_finder);
for(const auto& i : finder->do_find(node)) {
add_sym(i);
add_symbol(i);
}
delete finder;
}
void codegen::add_sym(const std::string& name) {
void codegen::add_symbol(const std::string& name) {
if (local.empty()) {
if (global.count(name)) {
return;
@@ -100,18 +101,19 @@ void codegen::gen(u8 operation_code, u32 num, u32 line) {
void codegen::num_gen(number_literal* node) {
f64 num = node->get_number();
regist_num(num);
gen(op_pnum,num_table.at(num), node->get_line());
gen(op_pnum,const_number_map.at(num), node->get_line());
}
void codegen::str_gen(string_literal* node) {
regist_str(node->get_content());
gen(op_pstr, str_table.at(node->get_content()), node->get_line());
const auto& str = node->get_content();
regist_str(str);
gen(op_pstr, const_string_map.at(str), node->get_line());
}
void codegen::bool_gen(bool_literal* node) {
f64 num = node->get_flag()? 1:0;
regist_num(num);
gen(op_pnum, num_table.at(num), node->get_line());
gen(op_pnum, const_number_map.at(num), node->get_line());
}
void codegen::vec_gen(vector_expr* node) {
@@ -127,7 +129,7 @@ void codegen::hash_gen(hash_expr* node) {
calc_gen(child->get_value());
const auto& field_name = child->get_name();
regist_str(field_name);
gen(op_happ, str_table.at(field_name), child->get_line());
gen(op_happ, const_string_map.at(field_name), child->get_line());
}
}
@@ -188,17 +190,17 @@ void codegen::func_gen(function* node) {
regist_str(name);
switch(tmp->get_parameter_type()) {
case parameter::param_type::normal_parameter:
gen(op_para, str_table.at(name), tmp->get_line());
gen(op_para, const_string_map.at(name), tmp->get_line());
break;
case parameter::param_type::default_parameter:
calc_gen(tmp->get_default_value());
gen(op_deft, str_table.at(name), tmp->get_line());
gen(op_deft, const_string_map.at(name), tmp->get_line());
break;
case parameter::param_type::dynamic_parameter:
gen(op_dyn, str_table.at(name), tmp->get_line());
gen(op_dyn, const_string_map.at(name), tmp->get_line());
break;
}
add_sym(name);
add_symbol(name);
}
code[newf].num = code.size()+1; // entry
@@ -235,8 +237,8 @@ void codegen::call_gen(call_expr* node) {
for(auto i : node->get_calls()) {
switch(i->get_type()) {
case expr_type::ast_callh: call_hash_gen((call_hash*)i); break;
case expr_type::ast_callv: call_vec((call_vector*)i); break;
case expr_type::ast_callf: call_func((call_function*)i); break;
case expr_type::ast_callv: call_vector_gen((call_vector*)i); break;
case expr_type::ast_callf: call_func_gen((call_function*)i); break;
default: break;
}
}
@@ -272,10 +274,10 @@ void codegen::call_id(identifier* node) {
void codegen::call_hash_gen(call_hash* node) {
regist_str(node->get_field());
gen(op_callh, str_table.at(node->get_field()), node->get_line());
gen(op_callh, const_string_map.at(node->get_field()), node->get_line());
}
void codegen::call_vec(call_vector* node) {
void codegen::call_vector_gen(call_vector* node) {
// maybe this place can use callv-const if ast's first child is ast_num
if (node->get_slices().size()==1 &&
!node->get_slices()[0]->get_end()) {
@@ -297,7 +299,7 @@ void codegen::call_vec(call_vector* node) {
gen(op_slcend, 0, node->get_line());
}
void codegen::call_func(call_function* node) {
void codegen::call_func_gen(call_function* node) {
if (node->get_argument().size() &&
node->get_argument()[0]->get_type()==expr_type::ast_pair) {
gen(op_newh, 0, node->get_line());
@@ -305,7 +307,7 @@ void codegen::call_func(call_function* node) {
calc_gen(((hash_pair*)child)->get_value());
const auto& field_name = ((hash_pair*)child)->get_name();
regist_str(field_name);
gen(op_happ, str_table.at(field_name), child->get_line());
gen(op_happ, const_string_map.at(field_name), child->get_line());
}
gen(op_callfh, 0, node->get_line());
} else {
@@ -340,8 +342,8 @@ void codegen::mcall(expr* node) {
auto tmp = call_node->get_calls()[i];
switch(tmp->get_type()) {
case expr_type::ast_callh: call_hash_gen((call_hash*)tmp); break;
case expr_type::ast_callv: call_vec((call_vector*)tmp); break;
case expr_type::ast_callf: call_func((call_function*)tmp); break;
case expr_type::ast_callv: call_vector_gen((call_vector*)tmp); break;
case expr_type::ast_callf: call_func_gen((call_function*)tmp); break;
default: break;
}
}
@@ -397,7 +399,7 @@ void codegen::mcall_vec(call_vector* node) {
void codegen::mcall_hash(call_hash* node) {
regist_str(node->get_field());
gen(op_mcallh, str_table.at(node->get_field()), node->get_line());
gen(op_mcallh, const_string_map.at(node->get_field()), node->get_line());
}
void codegen::single_def(definition_expr* node) {
@@ -411,12 +413,15 @@ void codegen::single_def(definition_expr* node) {
void codegen::multi_def(definition_expr* node) {
auto& identifiers = node->get_variables()->get_variables();
usize size = identifiers.size();
if (node->get_value()->get_type()==expr_type::ast_tuple) { // (var a,b,c)=(c,b,a);
auto& vals = ((tuple_expr*)node->get_value())->get_elements();
// (var a,b,c) = (c,b,a);
if (node->get_tuple()) {
auto& vals = node->get_tuple()->get_elements();
if (identifiers.size()<vals.size()) {
die("lack values in multi-definition", node->get_value()->get_location());
die("lack values in multi-definition",
node->get_tuple()->get_location());
} else if (identifiers.size()>vals.size()) {
die("too many values in multi-definition", node->get_value()->get_location());
die("too many values in multi-definition",
node->get_tuple()->get_location());
}
for(usize i = 0; i<size; ++i) {
calc_gen(vals[i]);
@@ -425,22 +430,22 @@ void codegen::multi_def(definition_expr* node) {
gen(op_loadg, global_find(name), identifiers[i]->get_line()):
gen(op_loadl, local_find(name), identifiers[i]->get_line());
}
} else { // (var a,b,c)=[0,1,2];
calc_gen(node->get_value());
for(usize i = 0; i<size; ++i) {
gen(op_callvi, i, node->get_value()->get_line());
const auto& name = identifiers[i]->get_name();
local.empty()?
gen(op_loadg, global_find(name), identifiers[i]->get_line()):
gen(op_loadl, local_find(name), identifiers[i]->get_line());
}
gen(op_pop, 0, node->get_line());
return;
}
// (var a,b,c) = [0,1,2];
calc_gen(node->get_value());
for(usize i = 0; i<size; ++i) {
gen(op_callvi, i, node->get_value()->get_line());
const auto& name = identifiers[i]->get_name();
local.empty()?
gen(op_loadg, global_find(name), identifiers[i]->get_line()):
gen(op_loadl, local_find(name), identifiers[i]->get_line());
}
gen(op_pop, 0, node->get_line());
}
void codegen::def_gen(definition_expr* node) {
if (node->get_variable_name() &&
node->get_value()->get_type()==expr_type::ast_tuple) {
if (node->get_variable_name() && node->get_tuple()) {
die("cannot accept too many values", node->get_value()->get_location());
}
node->get_variable_name()? single_def(node):multi_def(node);
@@ -463,7 +468,7 @@ void codegen::assignment_expression(assignment_expr* node) {
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_addeqc, num_table[num], node->get_line());
gen(op_addeqc, const_number_map[num], node->get_line());
}
break;
case assignment_expr::assign_type::sub_equal:
@@ -476,7 +481,7 @@ void codegen::assignment_expression(assignment_expr* node) {
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_subeqc, num_table[num], node->get_line());
gen(op_subeqc, const_number_map[num], node->get_line());
}
break;
case assignment_expr::assign_type::mult_equal:
@@ -489,7 +494,7 @@ void codegen::assignment_expression(assignment_expr* node) {
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_muleqc, num_table[num], node->get_line());
gen(op_muleqc, const_number_map[num], node->get_line());
}
break;
case assignment_expr::assign_type::div_equal:
@@ -502,7 +507,7 @@ void codegen::assignment_expression(assignment_expr* node) {
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_diveqc, num_table[num], node->get_line());
gen(op_diveqc, const_number_map[num], node->get_line());
}
break;
case assignment_expr::assign_type::concat_equal:
@@ -515,7 +520,7 @@ void codegen::assignment_expression(assignment_expr* node) {
} else {
const auto& str = ((string_literal*)node->get_right())->get_content();
regist_str(str);
gen(op_lnkeqc, str_table[str], node->get_line());
gen(op_lnkeqc, const_string_map[str], node->get_line());
}
break;
case assignment_expr::assign_type::bitwise_and_equal:
@@ -607,26 +612,26 @@ void codegen::multi_assign_gen(multi_assign* node) {
gen(op_meq, 1, tuple[i]->get_line());
}
}
} else {
calc_gen(node->get_value());
auto& tuple = node->get_tuple()->get_elements();
for(i32 i = 0; i<size; ++i) {
gen(op_callvi, i, node->get_value()->get_line());
// multi assign user loadl and loadg to avoid meq's stack--
// and this operation changes local and global value directly
mcall(tuple[i]);
if (code.back().op==op_mcalll) {
code.back().op=op_loadl;
} else if (code.back().op==op_mupval) {
code.back().op=op_loadu;
} else if (code.back().op==op_mcallg) {
code.back().op=op_loadg;
} else {
gen(op_meq, 1, tuple[i]->get_line());
}
}
gen(op_pop, 0, node->get_line());
return;
}
calc_gen(node->get_value());
auto& tuple = node->get_tuple()->get_elements();
for(i32 i = 0; i<size; ++i) {
gen(op_callvi, i, node->get_value()->get_line());
// multi assign user loadl and loadg to avoid meq's stack--
// and this operation changes local and global value directly
mcall(tuple[i]);
if (code.back().op==op_mcalll) {
code.back().op=op_loadl;
} else if (code.back().op==op_mupval) {
code.back().op=op_loadu;
} else if (code.back().op==op_mcallg) {
code.back().op=op_loadg;
} else {
gen(op_meq, 1, tuple[i]->get_line());
}
}
gen(op_pop, 0, node->get_line());
}
void codegen::cond_gen(condition_expr* node) {
@@ -706,7 +711,7 @@ void codegen::for_gen(for_expr* node) {
usize jmp_place = code.size();
if (node->get_condition()->get_type()==expr_type::ast_null) {
regist_num(1);
gen(op_pnum, num_table.at(1), node->get_condition()->get_line());
gen(op_pnum, const_number_map.at(1), node->get_condition()->get_line());
} else {
calc_gen(node->get_condition());
}
@@ -732,10 +737,11 @@ void codegen::forei_gen(forei_expr* node) {
gen(op_feach, 0, node->get_line());
}
if (node->get_iterator()->get_name()) { // define a new iterator
const auto& str = node->get_iterator()->get_name()->get_name();
auto name_node = node->get_iterator()->get_name();
const auto& str = name_node->get_name();
local.empty()?
gen(op_loadg, global_find(str), node->get_iterator()->get_name()->get_line()):
gen(op_loadl, local_find(str), node->get_iterator()->get_name()->get_line());
gen(op_loadg, global_find(str), name_node->get_line()):
gen(op_loadl, local_find(str), name_node->get_line());
} else { // use exist variable as the iterator
mcall(node->get_iterator()->get_call());
if (code.back().op==op_mcallg) {
@@ -760,15 +766,17 @@ void codegen::forei_gen(forei_expr* node) {
void codegen::statement_generation(expr* node) {
switch(node->get_type()) {
case expr_type::ast_null:break;
case expr_type::ast_null: break;
case expr_type::ast_def:
def_gen((definition_expr*)node); break;
case expr_type::ast_multi_assign:
multi_assign_gen((multi_assign*)node); break;
case expr_type::ast_assign:
assignment_statement((assignment_expr*)node); break;
case expr_type::ast_nil:case expr_type::ast_num:
case expr_type::ast_str:case expr_type::ast_bool: break;
case expr_type::ast_nil:
case expr_type::ast_num:
case expr_type::ast_str:
case expr_type::ast_bool: break;
case expr_type::ast_vec:
case expr_type::ast_hash:
case expr_type::ast_func:
@@ -785,35 +793,35 @@ void codegen::statement_generation(expr* node) {
void codegen::or_gen(binary_operator* node) {
calc_gen(node->get_left());
usize l1 = code.size();
usize label_jump_true_1 = code.size();
gen(op_jt, 0, node->get_left()->get_line());
gen(op_pop, 0, node->get_left()->get_line());
calc_gen(node->get_right());
usize l2=code.size();
usize label_jump_true_2 = code.size();
gen(op_jt, 0, node->get_right()->get_line());
gen(op_pop, 0, node->get_right()->get_line());
gen(op_pnil, 0, node->get_right()->get_line());
code[l1].num = code[l2].num = code.size();
code[label_jump_true_1].num = code[label_jump_true_2].num = code.size();
}
void codegen::and_gen(binary_operator* node) {
calc_gen(node->get_left());
gen(op_jt, code.size()+2, node->get_left()->get_line());
usize lfalse = code.size();
usize lable_jump_false = code.size();
gen(op_jmp, 0, node->get_left()->get_line());
gen(op_pop, 0, node->get_right()->get_line());// jt jumps here
gen(op_pop, 0, node->get_right()->get_line()); // jt jumps here
calc_gen(node->get_right());
gen(op_jt, code.size()+3, node->get_right()->get_line());
code[lfalse].num = code.size();
code[lable_jump_false].num = code.size();
gen(op_pop, 0, node->get_right()->get_line());
gen(op_pnil, 0, node->get_right()->get_line());
// jt jumps here
// jt jumps here, avoid pop and pnil
}
void codegen::unary_gen(unary_operator* node) {
@@ -881,7 +889,7 @@ void codegen::binary_gen(binary_operator* node) {
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_addc, num_table.at(num), node->get_line());
gen(op_addc, const_number_map.at(num), node->get_line());
}
return;
case binary_operator::binary_type::sub:
@@ -892,7 +900,7 @@ void codegen::binary_gen(binary_operator* node) {
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_subc, num_table.at(num), node->get_line());
gen(op_subc, const_number_map.at(num), node->get_line());
}
return;
case binary_operator::binary_type::mult:
@@ -903,7 +911,7 @@ void codegen::binary_gen(binary_operator* node) {
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_mulc, num_table.at(num), node->get_line());
gen(op_mulc, const_number_map.at(num), node->get_line());
}
return;
case binary_operator::binary_type::div:
@@ -914,7 +922,7 @@ void codegen::binary_gen(binary_operator* node) {
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_divc, num_table.at(num), node->get_line());
gen(op_divc, const_number_map.at(num), node->get_line());
}
return;
case binary_operator::binary_type::concat:
@@ -925,7 +933,7 @@ void codegen::binary_gen(binary_operator* node) {
} else {
const auto& str = ((string_literal*)node->get_right())->get_content();
regist_str(str);
gen(op_lnkc, str_table.at(str), node->get_line());
gen(op_lnkc, const_string_map.at(str), node->get_line());
}
break;
case binary_operator::binary_type::less:
@@ -936,7 +944,7 @@ void codegen::binary_gen(binary_operator* node) {
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_lessc, num_table.at(num), node->get_line());
gen(op_lessc, const_number_map.at(num), node->get_line());
}
return;
case binary_operator::binary_type::leq:
@@ -947,7 +955,7 @@ void codegen::binary_gen(binary_operator* node) {
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_leqc, num_table.at(num), node->get_line());
gen(op_leqc, const_number_map.at(num), node->get_line());
}
return;
case binary_operator::binary_type::grt:
@@ -958,7 +966,7 @@ void codegen::binary_gen(binary_operator* node) {
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_grtc, num_table.at(num), node->get_line());
gen(op_grtc, const_number_map.at(num), node->get_line());
}
return;
case binary_operator::binary_type::geq:
@@ -969,7 +977,7 @@ void codegen::binary_gen(binary_operator* node) {
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_geqc, num_table.at(num), node->get_line());
gen(op_geqc, const_number_map.at(num), node->get_line());
}
return;
default: break;
@@ -978,14 +986,14 @@ void codegen::binary_gen(binary_operator* node) {
void codegen::trino_gen(ternary_operator* node) {
calc_gen(node->get_condition());
usize lfalse = code.size();
usize label_jump_false = code.size();
gen(op_jf, 0, node->get_condition()->get_line());
calc_gen(node->get_left());
usize lexit = code.size();
usize label_jump_to_exit = code.size();
gen(op_jmp, 0, node->get_left()->get_line());
code[lfalse].num = code.size();
code[label_jump_false].num = code.size();
calc_gen(node->get_right());
code[lexit].num = code.size();
code[label_jump_to_exit].num = code.size();
}
void codegen::calc_gen(expr* node) {
@@ -1028,11 +1036,13 @@ void codegen::calc_gen(expr* node) {
void codegen::block_gen(code_block* node) {
for(auto tmp : node->get_expressions()) {
switch(tmp->get_type()) {
case expr_type::ast_null:break;
case expr_type::ast_null: break;
case expr_type::ast_id:
check_id_exist((identifier*)tmp); break;
case expr_type::ast_nil:case expr_type::ast_num:
case expr_type::ast_str:case expr_type::ast_bool:break;
case expr_type::ast_nil:
case expr_type::ast_num:
case expr_type::ast_str:
case expr_type::ast_bool: break;
case expr_type::ast_file_info:
// special node type in main block
fileindex = ((file_info*)tmp)->get_index(); break;
@@ -1087,73 +1097,76 @@ const error& codegen::compile(parse& parse, linker& import) {
gen(op_exit, 0, 0);
// size out of bound check
if (num_res.size()>0xffffff) {
err.load(file[0]); // load main execute file
err.load(file[0]); // load main execute file
if (const_number_table.size()>0xffffff) {
err.err("code",
"too many constant numbers: " + std::to_string(num_res.size()));
"too many constant numbers: " +
std::to_string(const_number_table.size()));
}
if (str_res.size()>0xffffff) {
err.load(file[0]); // load main execute file
if (const_string_table.size()>0xffffff) {
err.err("code",
"too many constant strings: " + std::to_string(str_res.size()));
"too many constant strings: " +
std::to_string(const_string_table.size()));
}
if (global.size()>=STACK_DEPTH) {
err.load(file[0]); // load main execute file
err.err("code",
"too many global variants: " + std::to_string(global.size()));
"too many global variables: " + std::to_string(global.size()));
}
if (code.size()>0xffffff) {
err.load(file[0]); // load main execute file
err.err("code",
"bytecode size overflow: " + std::to_string(code.size()));
}
return err;
}
void codegen::print() {
void codegen::print(std::ostream& out) {
// func end stack, reserved for code print
std::stack<u32> fbstk;
std::stack<u32> festk;
std::stack<u32> func_begin_stack;
std::stack<u32> func_end_stack;
// print const numbers
for(auto num : num_res) {
std::cout << " .number " << num << "\n";
for(auto num : const_number_table) {
out << " .number " << num << "\n";
}
// print const strings
for(const auto& str : str_res) {
std::cout << " .symbol \"" << rawstr(str) << "\"\n";
for(const auto& str : const_string_table) {
out << " .symbol \"" << rawstr(str) << "\"\n";
}
// print blank line
if (const_number_table.size() || const_string_table.size()) {
out << "\n";
}
// print code
std::cout<<"\n";
codestream::set(num_res.data(), str_res.data());
codestream::set(const_number_table.data(), const_string_table.data());
for(u32 i = 0; i<code.size(); ++i) {
// print opcode index, opcode name, opcode immediate number
const auto& c = code[i];
if (!festk.empty() && i==festk.top()) {
std::cout << std::hex << "<0x" << fbstk.top() << std::dec << ">;\n";
if (!func_end_stack.empty() && i==func_end_stack.top()) {
out << std::hex << "<0x" << func_begin_stack.top() << std::dec << ">;\n";
// avoid two empty lines
if (c.op!=op_newf) {
std::cout<<"\n";
out<<"\n";
}
fbstk.pop();
festk.pop();
func_begin_stack.pop();
func_end_stack.pop();
}
// get function begin index and end index
if (c.op==op_newf) {
std::cout << std::hex << "\nfunc <0x" << i << std::dec << ">:\n";
out << std::hex << "\nfunc <0x" << i << std::dec << ">:\n";
for(u32 j = i; j<code.size(); ++j) {
if (code[j].op==op_jmp) {
fbstk.push(i);
festk.push(code[j].num);
func_begin_stack.push(i);
func_end_stack.push(code[j].num);
break;
}
}
}
// output bytecode
std::cout << " " << codestream(c,i) << "\n";
out << " " << codestream(c,i) << "\n";
}
}

View File

@@ -25,10 +25,10 @@ private:
error& err;
const std::string* file;
std::stack<u32> in_iterloop;
std::unordered_map<f64,u32> num_table;
std::unordered_map<std::string,u32> str_table;
std::vector<f64> num_res;
std::vector<std::string> str_res;
std::unordered_map<f64,u32> const_number_map;
std::unordered_map<std::string,u32> const_string_map;
std::vector<f64> const_number_table;
std::vector<std::string> const_string_table;
std::vector<opcode> code;
std::list<std::vector<i32>> continue_ptr;
std::list<std::vector<i32>> break_ptr;
@@ -49,7 +49,7 @@ private:
void regist_num(const f64);
void regist_str(const std::string&);
void find_symbol(code_block*);
void add_sym(const std::string&);
void add_symbol(const std::string&);
i32 local_find(const std::string&);
i32 global_find(const std::string&);
i32 upvalue_find(const std::string&);
@@ -65,8 +65,8 @@ private:
void call_gen(call_expr*);
void call_id(identifier*);
void call_hash_gen(call_hash*);
void call_vec(call_vector*);
void call_func(call_function*);
void call_vector_gen(call_vector*);
void call_func_gen(call_function*);
void mcall(expr*);
void mcall_id(identifier*);
void mcall_vec(call_vector*);
@@ -93,11 +93,13 @@ private:
void block_gen(code_block*);
void ret_gen(return_expr*);
public:
const std::vector<std::string>& strs() const {return const_string_table;}
const std::vector<f64>& nums() const {return const_number_table;}
const std::vector<opcode>& codes() const {return code;}
public:
codegen(error& e): fileindex(0), err(e), file(nullptr) {}
const error& compile(parse&, linker&);
void print();
const std::vector<std::string>& strs() const {return str_res;}
const std::vector<f64>& nums() const {return num_res;}
const std::vector<opcode>& codes() const {return code;}
void print(std::ostream&);
};

View File

@@ -26,33 +26,33 @@ void dylib_destructor(void* ptr) {
void func_addr_destructor(void* ptr) {}
var nas_vec::get_val(const i32 n) {
i32 size=elems.size();
i32 size = elems.size();
if (n<-size || n>=size) {
return var::none();
}
return elems[n>=0?n:n+size];
return elems[n>=0? n:n+size];
}
var* nas_vec::get_mem(const i32 n) {
i32 size=elems.size();
i32 size = elems.size();
if (n<-size || n>=size) {
return nullptr;
}
return &elems[n>=0?n:n+size];
return &elems[n>=0? n:n+size];
}
std::ostream& operator<<(std::ostream& out, nas_vec& vec) {
if (!vec.elems.size() || vec.printed) {
out<<(vec.elems.size()?"[..]":"[]");
out << (vec.elems.size()? "[..]":"[]");
return out;
}
vec.printed=true;
usize iter=0,size=vec.elems.size();
out<<'[';
vec.printed = true;
usize iter = 0, size = vec.elems.size();
out << "[";
for(auto& i:vec.elems) {
out<<i<<",]"[(++iter)==size];
out << i << ",]"[(++iter)==size];
}
vec.printed=false;
vec.printed = false;
return out;
}
@@ -62,14 +62,14 @@ var nas_hash::get_val(const std::string& key) {
} else if (!elems.count("parents")) {
return var::none();
}
var ret=var::none();
var val=elems.at("parents");
var ret = var::none();
var val = elems.at("parents");
if (val.type!=vm_vec) {
return ret;
}
for(auto& i:val.vec().elems) {
for(auto& i : val.vec().elems) {
if (i.type==vm_hash) {
ret=i.hash().get_val(key);
ret = i.hash().get_val(key);
}
if (ret.type!=vm_none) {
return ret;
@@ -84,14 +84,14 @@ var* nas_hash::get_mem(const std::string& key) {
} else if (!elems.count("parents")) {
return nullptr;
}
var* addr=nullptr;
var val=elems.at("parents");
var* addr = nullptr;
var val = elems.at("parents");
if (val.type!=vm_vec) {
return addr;
}
for(auto& i:val.vec().elems) {
for(auto& i : val.vec().elems) {
if (i.type==vm_hash) {
addr=i.hash().get_mem(key);
addr = i.hash().get_mem(key);
}
if (addr) {
return addr;
@@ -102,30 +102,31 @@ var* nas_hash::get_mem(const std::string& key) {
std::ostream& operator<<(std::ostream& out, nas_hash& hash) {
if (!hash.elems.size() || hash.printed) {
out<<(hash.elems.size()?"{..}":"{}");
out << (hash.elems.size()? "{..}":"{}");
return out;
}
hash.printed=true;
usize iter=0,size=hash.elems.size();
out<<'{';
for(auto& i:hash.elems) {
out<<i.first<<':'<<i.second<<",}"[(++iter)==size];
hash.printed = true;
usize iter = 0, size = hash.elems.size();
out << "{";
for(auto& i : hash.elems) {
out << i.first << ":" << i.second << ",}"[(++iter)==size];
}
hash.printed=false;
hash.printed = false;
return out;
}
void nas_func::clear() {
dpara=-1;
dpara = -1;
local.clear();
upval.clear();
keys.clear();
}
void nas_ghost::set(usize t, void* p, ghost_register_table* table) {
type=t;
ptr=p;
ghost_type_table=table;
void nas_ghost::set(
usize ghost_type, void* ghost_pointer, ghost_register_table* table) {
type = ghost_type;
ptr = ghost_pointer;
ghost_type_table = table;
}
void nas_ghost::clear() {
@@ -133,37 +134,37 @@ void nas_ghost::clear() {
return;
}
ghost_type_table->destructor(type)(ptr);
ptr=nullptr;
ptr = nullptr;
}
void nas_co::clear() {
for(u32 i=0;i<STACK_DEPTH;++i) {
stack[i]=var::nil();
for(u32 i = 0; i<STACK_DEPTH; ++i) {
stack[i] = var::nil();
}
ctx.pc=0;
ctx.localr=nullptr;
ctx.memr=nullptr;
ctx.canary=stack+STACK_DEPTH-1;
ctx.top=stack;
ctx.funcr=var::nil();
ctx.upvalr=var::nil();
ctx.stack=stack;
ctx.pc = 0;
ctx.localr = nullptr;
ctx.memr = nullptr;
ctx.canary = stack+STACK_DEPTH-1;
ctx.top = stack;
ctx.funcr = var::nil();
ctx.upvalr = var::nil();
ctx.stack = stack;
status=coroutine_status::suspended;
status = coroutine_status::suspended;
}
nas_val::nas_val(u8 val_type) {
mark=gc_status::collected;
type=val_type;
unmut=0;
mark = gc_status::collected;
type = val_type;
unmut = 0;
switch(val_type) {
case vm_str: ptr.str=new std::string; break;
case vm_vec: ptr.vec=new nas_vec; break;
case vm_hash: ptr.hash=new nas_hash; break;
case vm_func: ptr.func=new nas_func; break;
case vm_upval: ptr.upval=new nas_upval; break;
case vm_obj: ptr.obj=new nas_ghost; break;
case vm_co: ptr.co=new nas_co; break;
case vm_str: ptr.str = new std::string; break;
case vm_vec: ptr.vec = new nas_vec; break;
case vm_hash: ptr.hash = new nas_hash; break;
case vm_func: ptr.func = new nas_func; break;
case vm_upval: ptr.upval = new nas_upval; break;
case vm_obj: ptr.obj = new nas_ghost; break;
case vm_co: ptr.co = new nas_co; break;
}
}
@@ -210,15 +211,15 @@ std::string var::tostr() {
std::ostream& operator<<(std::ostream& out, var& ref) {
switch(ref.type) {
case vm_none: out<<"undefined"; break;
case vm_nil: out<<"nil"; break;
case vm_num: out<<ref.val.num; break;
case vm_str: out<<ref.str(); break;
case vm_vec: out<<ref.vec(); break;
case vm_hash: out<<ref.hash(); break;
case vm_func: out<<"func(..) {..}";break;
case vm_obj: out<<ref.obj(); break;
case vm_co: out<<"<coroutine>"; break;
case vm_none: out << "undefined"; break;
case vm_nil: out << "nil"; break;
case vm_num: out << ref.val.num; break;
case vm_str: out << ref.str(); break;
case vm_vec: out << ref.vec(); break;
case vm_hash: out << ref.hash(); break;
case vm_func: out << "func(..) {..}"; break;
case vm_obj: out << ref.obj(); break;
case vm_co: out << "<coroutine>"; break;
}
return out;
}
@@ -304,7 +305,7 @@ void gc::mark() {
mark_context(bfs);
while(!bfs.empty()) {
var value=bfs.back();
var value = bfs.back();
bfs.pop_back();
if (value.type<=vm_num ||
value.val.gcobj->mark!=gc_status::uncollected) {
@@ -317,7 +318,7 @@ void gc::mark() {
void gc::mark_context(std::vector<var>& bfs_queue) {
// scan now running context, this context maybe related to coroutine or main
for(var* i=rctx->stack;i<=rctx->top;++i) {
for(var* i = rctx->stack; i<=rctx->top; ++i) {
bfs_queue.push_back(*i);
}
bfs_queue.push_back(rctx->funcr);
@@ -329,7 +330,7 @@ void gc::mark_context(std::vector<var>& bfs_queue) {
}
// coroutine is running, so scan main process stack from mctx
for(var* i=mctx.stack;i<=mctx.top;++i) {
for(var* i = mctx.stack; i<=mctx.top; ++i) {
bfs_queue.push_back(*i);
}
bfs_queue.push_back(mctx.funcr);
@@ -337,7 +338,7 @@ void gc::mark_context(std::vector<var>& bfs_queue) {
}
void gc::mark_var(std::vector<var>& bfs_queue, var& value) {
value.val.gcobj->mark=gc_status::found;
value.val.gcobj->mark = gc_status::found;
switch(value.type) {
case vm_vec: mark_vec(bfs_queue, value.vec()); break;
case vm_hash: mark_hash(bfs_queue, value.hash()); break;
@@ -349,28 +350,28 @@ void gc::mark_var(std::vector<var>& bfs_queue, var& value) {
}
void gc::mark_vec(std::vector<var>& bfs_queue, nas_vec& vec) {
for(auto& i:vec.elems) {
for(auto& i : vec.elems) {
bfs_queue.push_back(i);
}
}
void gc::mark_hash(std::vector<var>& bfs_queue, nas_hash& hash) {
for(auto& i:hash.elems) {
for(auto& i : hash.elems) {
bfs_queue.push_back(i.second);
}
}
void gc::mark_func(std::vector<var>& bfs_queue, nas_func& function) {
for(auto& i:function.local) {
for(auto& i : function.local) {
bfs_queue.push_back(i);
}
for(auto& i:function.upval) {
for(auto& i : function.upval) {
bfs_queue.push_back(i);
}
}
void gc::mark_upval(std::vector<var>& bfs_queue, nas_upval& upval) {
for(auto& i:upval.elems) {
for(auto& i : upval.elems) {
bfs_queue.push_back(i);
}
}
@@ -378,84 +379,79 @@ void gc::mark_upval(std::vector<var>& bfs_queue, nas_upval& upval) {
void gc::mark_co(std::vector<var>& bfs_queue, nas_co& co) {
bfs_queue.push_back(co.ctx.funcr);
bfs_queue.push_back(co.ctx.upvalr);
for(var* i=co.stack;i<=co.ctx.top;++i) {
for(var* i = co.stack; i<=co.ctx.top; ++i) {
bfs_queue.push_back(*i);
}
}
void gc::sweep() {
for(auto i:memory) {
for(auto i : memory) {
if (i->mark==gc_status::uncollected) {
i->clear();
unused[i->type-vm_str].push_back(i);
i->mark=gc_status::collected;
i->mark = gc_status::collected;
} else if (i->mark==gc_status::found) {
i->mark=gc_status::uncollected;
i->mark = gc_status::uncollected;
}
}
}
void gc::extend(u8 type) {
const u8 index=type-vm_str;
size[index]+=incr[index];
const u8 index = type-vm_str;
size[index] += incr[index];
for(u32 i=0;i<incr[index];++i) {
nas_val* tmp=new(std::nothrow) nas_val(type);
if (!tmp) {
std::cerr<<"nasal_gc.h: gc::extend: ";
std::cerr<<"failed to allocate memory\n";
std::exit(1);
}
for(u32 i = 0; i<incr[index]; ++i) {
// no need to check, will be killed if memory is not enough
nas_val* tmp = new nas_val(type);
// add to heap
memory.push_back(tmp);
unused[index].push_back(tmp);
}
incr[index]=incr[index]+incr[index]/2;
incr[index] = incr[index]+incr[index]/2;
}
void gc::init(
const std::vector<std::string>& s, const std::vector<std::string>& argv) {
// initialize function register
rctx->funcr=nil;
worktime=0;
rctx->funcr = nil;
worktime = 0;
// initialize counters
for(u8 i=0;i<gc_type_size;++i) {
size[i]=gcnt[i]=acnt[i]=0;
for(u8 i = 0; i<gc_type_size; ++i) {
size[i] = gcnt[i] = acnt[i] = 0;
}
// coroutine pointer set to nullptr
cort=nullptr;
cort = nullptr;
// init constant strings
strs.resize(s.size());
for(u32 i=0;i<strs.size();++i) {
for(u32 i = 0; i<strs.size(); ++i) {
strs[i]=var::gcobj(new nas_val(vm_str));
strs[i].val.gcobj->unmut=1;
strs[i].str()=s[i];
strs[i].val.gcobj->unmut = 1;
strs[i].str() = s[i];
}
// record arguments
env_argv.resize(argv.size());
for(usize i=0;i<argv.size();++i) {
env_argv[i]=var::gcobj(new nas_val(vm_str));
env_argv[i].val.gcobj->unmut=1;
env_argv[i].str()=argv[i];
for(usize i = 0; i<argv.size(); ++i) {
env_argv[i] = var::gcobj(new nas_val(vm_str));
env_argv[i].val.gcobj->unmut = 1;
env_argv[i].str() = argv[i];
}
}
void gc::clear() {
for(auto i:memory) {
for(auto i : memory) {
delete i;
}
memory.clear();
for(u8 i=0;i<gc_type_size;++i) {
for(u8 i = 0; i<gc_type_size; ++i) {
unused[i].clear();
}
for(auto& i:strs) {
for(auto& i : strs) {
delete i.val.gcobj;
}
strs.clear();
@@ -476,8 +472,8 @@ void gc::info() {
"coroutine"
};
usize indent=0;
for(u8 i=0;i<gc_type_size;++i) {
usize indent = 0;
for(u8 i = 0; i<gc_type_size; ++i) {
usize len = 0;
len = std::to_string(gcnt[i]).length();
indent = indent<len? len:indent;
@@ -487,7 +483,7 @@ void gc::info() {
indent = indent<len? len:indent;
}
double total=0;
double total = 0;
std::clog << "\ngc info (gc count|alloc count|memory size)\n";
for(u8 i = 0; i<gc_type_size; ++i) {
if (!gcnt[i] && !acnt[i] && !size[i]) {
@@ -535,43 +531,43 @@ var gc::alloc(u8 type) {
if (unused[index].empty()) {
extend(type);
}
var ret=var::gcobj(unused[index].back());
ret.val.gcobj->mark=gc_status::uncollected;
var ret = var::gcobj(unused[index].back());
ret.val.gcobj->mark = gc_status::uncollected;
unused[index].pop_back();
return ret;
}
void gc::ctxchg(nas_co& co) {
// store running state to main context
mctx=*rctx;
mctx = *rctx;
// restore coroutine context state
*rctx=co.ctx;
*rctx = co.ctx;
// set coroutine pointer
cort=&co;
cort = &co;
// set coroutine state to running
cort->status=coroutine_status::running;
cort->status = coroutine_status::running;
}
void gc::ctxreserve() {
// pc=0 means this coroutine is finished
cort->status=rctx->pc?
cort->status = rctx->pc?
coroutine_status::suspended:
coroutine_status::dead;
// store running state to coroutine
cort->ctx=*rctx;
cort->ctx = *rctx;
// restore main context state
*rctx=mctx;
*rctx = mctx;
// set coroutine pointer to nullptr
cort=nullptr;
cort = nullptr;
}
var nas_err(const std::string& error_function_name, const std::string& info) {
std::cerr<<"[vm] "<<error_function_name<<": "<<info<<"\n";
std::cerr << "[vm] " << error_function_name << ": " << info << "\n";
return var::none();
}

View File

@@ -50,7 +50,7 @@ enum vm_type:u8 {
vm_co
};
const u32 gc_type_size=vm_co-vm_str+1;
const u32 gc_type_size = vm_co-vm_str+1;
enum class coroutine_status:u32 {
suspended,
@@ -84,11 +84,11 @@ public:
} val;
private:
var(u8 t, u32 pc) {type=t;val.ret=pc;}
var(u8 t, i64 ct) {type=t;val.cnt=ct;}
var(u8 t, f64 n) {type=t;val.num=n;}
var(u8 t, var* p) {type=t;val.addr=p;}
var(u8 t, nas_val* p) {type=t;val.gcobj=p;}
var(u8 t, u32 pc) {type = t; val.ret = pc;}
var(u8 t, i64 ct) {type = t; val.cnt = ct;}
var(u8 t, f64 n) {type = t; val.num = n;}
var(u8 t, var* p) {type = t; val.addr = p;}
var(u8 t, nas_val* p) {type = t; val.gcobj = p;}
public:
var() = default;
@@ -147,7 +147,7 @@ struct nas_hash {
// mark if this is printed, avoid stackoverflow
bool printed;
nas_hash():printed(false) {}
nas_hash(): printed(false) {}
usize size() const {return elems.size();}
var get_val(const std::string&);
var* get_mem(const std::string&);
@@ -184,9 +184,9 @@ public:
}
void clear() {
onstk=true;
onstk = true;
elems.clear();
size=0;
size = 0;
}
};
@@ -233,12 +233,12 @@ public:
usize register_ghost_type(const std::string& name, dtor ptr) {
if (mapper.count(name)) {
std::cerr<<"nasal_gc.h: ghost_register_table::register_ghost_type: ";
std::cerr<<"ghost type \""<<name<<"\" already exists.\n";
std::cerr << "nasal_gc.h: ghost_register_table::register_ghost_type: ";
std::cerr << "ghost type \"" << name << "\" already exists.\n";
std::exit(1);
}
auto res=destructors.size();
mapper[name]=res;
auto res = destructors.size();
mapper[name] = res;
ghost_name.push_back(name);
destructors.push_back(ptr);
return res;
@@ -265,8 +265,8 @@ public:
public:
friend std::ostream& operator<<(std::ostream& out, nas_ghost& ghost) {
out<<"<object "<<ghost.ghost_type_table->get_ghost_name(ghost.type);
out<<" at 0x"<<std::hex<<(u64)ghost.ptr<<std::dec<<">";
out << "<object " << ghost.ghost_type_table->get_ghost_name(ghost.type);
out << " at 0x" << std::hex << (u64)ghost.ptr << std::dec << ">";
return out;
}
@@ -332,7 +332,7 @@ struct gc {
nas_co* cort = nullptr; // running coroutine
/* temporary space used in builtin/module functions */
var temp=nil;
var temp = nil;
/* constants and memory pool */
std::vector<var> strs; // reserved address for const vm_str
@@ -385,20 +385,20 @@ public:
public:
var newstr(char c) {
var s=alloc(vm_str);
s.str()=c;
var s = alloc(vm_str);
s.str() = c;
return s;
}
var newstr(const char* buff) {
var s=alloc(vm_str);
s.str()=buff;
s.str() = buff;
return s;
}
var newstr(const std::string& buff) {
var s=alloc(vm_str);
s.str()=buff;
s.str() = buff;
return s;
}
};

View File

@@ -1,35 +1,34 @@
#include "nasal_opcode.h"
const char* opname[]={
"exit ","intg ","intl ","loadg ",
"loadl ","loadu ","pnum ","pnil ",
"pstr ","newv ","newh ","newf ",
"happ ","para ","def ","dyn ",
"lnot ","usub ","bnot ","btor ",
"btxor ","btand ","add ","sub ",
"mult ","div ","lnk ","addc ",
"subc ","multc ","divc ","lnkc ",
"addeq ","subeq ","muleq ","diveq ",
"lnkeq ","bandeq","boreq ","bxoreq",
"addeqc","subeqc","muleqc","diveqc",
"lnkeqc","addecp","subecp","mulecp",
"divecp","lnkecp","meq ","eq ",
"neq ","less ","leq ","grt ",
"geq ","lessc ","leqc ","grtc ",
"geqc ","pop ","jmp ","jt ",
"jf ","cnt ","findx ","feach ",
"callg ","calll ","upval ","callv ",
"callvi","callh ","callfv","callfh",
"callb ","slcbeg","slcend","slc ",
"slc2 ","mcallg","mcalll","mupval",
"mcallv","mcallh","ret "
const char* opname[] = {
"exit ", "intg ", "intl ", "loadg ",
"loadl ", "loadu ", "pnum ", "pnil ",
"pstr ", "newv ", "newh ", "newf ",
"happ ", "para ", "def ", "dyn ",
"lnot ", "usub ", "bitnot", "bitor ",
"bitxor", "bitand", "add ", "sub ",
"mult ", "div ", "lnk ", "addc ",
"subc ", "multc ", "divc ", "lnkc ",
"addeq ", "subeq ", "muleq ", "diveq ",
"lnkeq ", "bandeq", "boreq ", "bxoreq",
"addeqc", "subeqc", "muleqc", "diveqc",
"lnkeqc", "addecp", "subecp", "mulecp",
"divecp", "lnkecp", "meq ", "eq ",
"neq ", "less ", "leq ", "grt ",
"geq ", "lessc ", "leqc ", "grtc ",
"geqc ", "pop ", "jmp ", "jt ",
"jf ", "cnt ", "findx ", "feach ",
"callg ", "calll ", "upval ", "callv ",
"callvi", "callh ", "callfv", "callfh",
"callb ", "slcbeg", "slcend", "slice ",
"slice2", "mcallg", "mcalll", "mupval",
"mcallv", "mcallh", "ret "
};
void codestream::set(
const f64* numbuff,
const std::string* strbuff,
const std::string* filelist
) {
const std::string* filelist) {
nums = numbuff;
strs = strbuff;
files = filelist;
@@ -40,14 +39,14 @@ void codestream::dump(std::ostream& out) const {
using std::setfill;
using std::hex;
using std::dec;
auto op=code.op;
auto num=code.num;
out<<hex<<"0x"
<<setw(6)<<setfill('0')<<index<<" "
<<setw(2)<<setfill('0')<<(u32)op<<" "
<<setw(2)<<setfill('0')<<((num>>16)&0xff)<<" "
<<setw(2)<<setfill('0')<<((num>>8)&0xff)<<" "
<<setw(2)<<setfill('0')<<(num&0xff)<<" "
auto op = code.op;
auto num = code.num;
out << hex << "0x"
<< setw(6) << setfill('0') << index << " "
<< setw(2) << setfill('0') << (u32)op << " "
<< setw(2) << setfill('0') << ((num>>16)&0xff) << " "
<< setw(2) << setfill('0') << ((num>>8)&0xff) << " "
<< setw(2) << setfill('0') << (num&0xff) << " "
<<opname[op]<<" "<<dec;
switch(op) {
case op_addeq: case op_subeq:
@@ -55,23 +54,28 @@ void codestream::dump(std::ostream& out) const {
case op_lnkeq: case op_meq:
case op_btandeq: case op_btoreq:
case op_btxoreq:
out<<hex<<"0x"<<num<<dec<<" sp-"<<num;break;
out << hex << "0x" << num << dec << " sp-" << num; break;
case op_addeqc: case op_subeqc:
case op_muleqc:case op_diveqc:
out<<hex<<"0x"<<num<<dec<<" ("<<nums[num]<<")";break;
out << hex << "0x" << num << dec
<< " (" << nums[num] << ")"; break;
case op_lnkeqc:
out<<hex<<"0x"<<num<<dec<<" ("<<rawstr(strs[num], 16)<<")";break;
out << hex << "0x" << num << dec
<< " (" << rawstr(strs[num], 16) << ")"; break;
case op_addecp: case op_subecp:
case op_mulecp: case op_divecp:
out<<hex<<"0x"<<num<<dec<<" ("<<nums[num]<<") sp-1";break;
out << hex << "0x" << num << dec
<< " (" << nums[num] << ") sp-1"; break;
case op_lnkecp:
out<<hex<<"0x"<<num<<dec<<" ("<<rawstr(strs[num], 16)<<") sp-1";break;
out << hex << "0x" << num << dec
<< " (" << rawstr(strs[num], 16) << ") sp-1"; break;
case op_addc: case op_subc:
case op_mulc: case op_divc:
case op_lessc: case op_leqc:
case op_grtc: case op_geqc:
case op_pnum:
out<<hex<<"0x"<<num<<dec<<" ("<<nums[num]<<")";break;
out << hex << "0x" << num << dec
<< " (" << nums[num] << ")"; break;
case op_callvi: case op_newv:
case op_callfv: case op_intg:
case op_intl: case op_findex:
@@ -81,27 +85,28 @@ void codestream::dump(std::ostream& out) const {
case op_mcallg: case op_loadg:
case op_calll: case op_mcalll:
case op_loadl:
out<<hex<<"0x"<<num<<dec;break;
out << hex << "0x" << num << dec; break;
case op_callb:
out<<hex<<"0x"<<num<<" <"<<builtin[num].name
<<"@0x"<<(u64)builtin[num].func<<dec<<">";break;
out << hex << "0x" << num << " <" << builtin[num].name
<< "@0x" << (u64)builtin[num].func << dec << ">"; break;
case op_upval: case op_mupval:
case op_loadu:
out<<hex<<"0x"<<((num>>16)&0xffff)
<<"[0x"<<(num&0xffff)<<"]"<<dec;break;
out << hex << "0x" << ((num>>16)&0xffff)
<< "[0x" << (num&0xffff) << "]" << dec; break;
case op_happ: case op_pstr:
case op_lnkc: case op_callh:
case op_mcallh: case op_para:
case op_deft: case op_dyn:
out<<hex<<"0x"<<num<<dec<<" ("<<rawstr(strs[num], 16)<<")";break;
out << hex << "0x" << num << dec
<< " (" << rawstr(strs[num], 16) << ")"; break;
default:
if (files) {
out<<hex<<"0x"<<num<<dec;
out << hex << "0x" << num << dec;
}
break;
}
if (files) {
out<<"("<<files[code.fidx]<<":"<<code.line<<")";
out << "(" << files[code.fidx] << ":" << code.line << ")";
}
}

View File

@@ -314,7 +314,7 @@ void parse::params(function* func_node) {
expr* parse::lcurve_expr() {
if (toks[ptr+1].type==tok::var)
return definition();
return check_tuple()?multi_assignment():calc();
return check_tuple()? multi_assignment():calc();
}
expr* parse::expression() {
@@ -639,7 +639,7 @@ expr* parse::scalar() {
return node;
}
expr* parse::call_scalar() {
call* parse::call_scalar() {
switch(toks[ptr].type) {
case tok::lcurve: return callf(); break;
case tok::lbracket: return callv(); break;
@@ -647,7 +647,7 @@ expr* parse::call_scalar() {
default: break;
}
// unreachable
return null();
return new call(toks[ptr].loc, expr_type::ast_null);
}
call_hash* parse::callh() {
@@ -741,7 +741,9 @@ expr* parse::definition() {
}
match(tok::eq);
if (lookahead(tok::lcurve)) {
node->set_value(check_tuple()?multi_scalar():calc());
check_tuple()?
node->set_tuple(multi_scalar()):
node->set_value(calc());
} else {
node->set_value(calc());
}

View File

@@ -114,7 +114,7 @@ private:
expr* multive_expr();
unary_operator* unary();
expr* scalar();
expr* call_scalar();
call* call_scalar();
call_hash* callh();
call_vector* callv();
call_function* callf();

View File

@@ -8,7 +8,11 @@ bool symbol_finder::visit_definition_expr(definition_expr* node) {
symbols.push_back(i->get_name());
}
}
node->get_value()->accept(this);
if (node->get_tuple()) {
node->get_tuple()->accept(this);
} else {
node->get_value()->accept(this);
}
return true;
}

View File

@@ -3,34 +3,34 @@
# print is used to print all things in nasal, try and see how it works.
# this function uses std::cout to output logs.
var print=func(elems...){
var print = func(elems...) {
return __print(elems);
}
# append is used to add values into a vector.
var append=func(vec,elems...){
return __append(vec,elems);
var append = func(vec, elems...) {
return __append(vec, elems);
}
# setsize is used to change the size of vector.
# if the size is larger than before,
# this function will fill vm_nil into uninitialized space.
var setsize=func(vec,size){
return __setsize(vec,size);
var setsize = func(vec, size) {
return __setsize(vec, size);
}
# system has the same use in C.
var system=func(str){
var system = func(str) {
return __system(str);
}
# input uses std::cin and returns what we input.
var input=func(end=nil){
var input = func(end = nil) {
return __input(end);
}
# readline
var readline=func(prompt="> ") {
var readline = func(prompt = "> ") {
print(prompt);
return input("\n");
}
@@ -38,224 +38,224 @@ var readline=func(prompt="> ") {
# split a string by separator for example:
# split("ll","hello world") -> ["he","o world"]
# this function will return a vector.
var split=func(separator,str){
return __split(separator,str);
var split = func(separator, str) {
return __split(separator, str);
}
# rand has the same function as the rand in C
# if seed is nil, it will return the random number.
# if seed is not nil, it will be initialized by this seed.
var rand=func(seed=nil){
var rand = func(seed = nil) {
return __rand(seed);
}
# id will return the pointer of an gc-object.
# if this object is not managed by gc, it will return 0.
var id=func(object){
var id = func(object) {
return __id(object);
}
# int will get the integer of input number/string.
# but carefully use it, because int has range between -2147483648~2147483647
var int=func(val){
var int = func(val) {
return __int(val);
}
# floor will get the integral number of input argument
# which is less than or equal to this argument
var floor=func(val){
var floor = func(val) {
return __floor(val);
}
# exit using std::exit
var exit=func(val=-1){
var exit = func(val = -1) {
return __exit(val);
}
# abort using std::abort
var abort=func(){
var abort = func() {
__abort();
}
# abs gets absolute number.
var abs=func(n){
return n>0?n:-n;
var abs = func(n) {
return n>0? n:-n;
}
# num will change all the other types into number.
# mostly used to change a numerable string.
var num=func(val){
var num = func(val) {
return __num(val);
}
# pop used to pop the last element in a vector.
# this function will return the value that poped if vector has element(s).
# if the vector is empty, it will return nil.
var pop=func(vec){
var pop = func(vec) {
return __pop(vec);
}
# str is used to change number into string.
var str=func(num){
var str = func(num) {
return __str(num);
}
# size can get the size of a string/vector/hashmap.
# in fact it can also get the size of number, and the result is the number itself.
# so don't do useless things, though it really works.
var size=func(object){
var size = func(object) {
return __size(object);
}
# contains is used to check if a key exists in a hashmap/dict.
var contains=func(hash,key){
return __contains(hash,key);
var contains = func(hash, key) {
return __contains(hash, key);
}
# delete is used to delete a pair in a hashmap/dict by key.
var delete=func(hash,key){
return __delete(hash,key);
var delete = func(hash, key) {
return __delete(hash, key);
}
# keys is used to get all keys in a hashmap/dict.
# this function will return a vector.
var keys=func(hash){
var keys = func(hash) {
return __keys(hash);
}
# time has the same function in C.
var time=func(begin){
var time = func(begin) {
return __time(begin);
}
var systime=func(){
var systime = func() {
return time(0);
}
# die is a special native function.
# use it at where you want the program to crash immediately.
var die=func(str){
var die = func(str = "error occurred.") {
return __die(str);
}
# find will give the first position of the needle in haystack
var find=func(needle,haystack){
return __find(needle,haystack);
var find = func(needle, haystack) {
return __find(needle, haystack);
}
# typeof is used to get the type of an object.
# this function returns a string.
var typeof=func(object){
var typeof = func(object) {
return __type(object);
}
# subvec is used to get part of a vector
var subvec=func(vec,begin,length=nil){
return vec[begin:(length==nil?nil:begin+length-1)];
var subvec = func(vec, begin, length = nil) {
return vec[begin:(length==nil? nil:begin+length-1)];
}
# substr will get the sub-string.
# it gets the string, the begin index and sub-string's length as arguments.
var substr=func(str,begin,len){
return __substr(str,begin,len);
var substr = func(str, begin, len) {
return __substr(str, begin, len);
}
# streq is used to compare if two strings are the same.
var streq=func(a,b){
return __streq(a,b);
var streq = func(a, b) {
return __streq(a, b);
}
# left is used to get the sub-string like substr.
# but the begin index is 0.
var left=func(str,len){
return __left(str,len);
var left = func(str, len) {
return __left(str, len);
}
# right i used to get the sub-string like substr.
# but the begin index is strlen-len.
var right=func(str,len){
return __right(str,len);
var right = func(str, len) {
return __right(str, len);
}
# cmp is used to compare two strings.
# normal string will not be correctly compared by operators < > <= >=
# because these operators will turn strings into numbers then compare.
var cmp=func(a,b){
var cmp = func(a, b) {
return __cmp(a,b);
}
# chr is used to get the character by ascii-number.
# for example chr(65) -> 'A'
var chr=func(code){
var chr = func(code) {
return __chr(code);
}
# char will give you the real character of ascii-number
# instead of extend-ascii when number between 128~256
var char=func(code){
var char = func(code) {
return __char(code);
}
# mut is used to change unmutable strings to mutable.
var mut=func(str){
var mut = func(str) {
return str~"";
}
# srand wraps up rand, using time(0) as the seed.
var srand=func(){
var srand = func() {
rand(time(0));
return 0;
}
# values() gets all values in a hash.
var values=func(hash){
var values = func(hash) {
return __values(hash);
}
# println has the same function as print.
# but it will output a '\n' after using print.
var println=func(elems...){
var println = func(elems...) {
return __println(elems);
}
var isfunc=func(f){
var isfunc = func(f) {
return typeof(f)=="func";
}
var isghost=func(g){
var isghost = func(g) {
return typeof(g)=="obj";
}
var ishash=func(h){
var ishash = func(h) {
return typeof(h)=="hash";
}
var isint=func(x){
var isint = func(x) {
return x==floor(x);
}
var isnum=func(x){
var isnum = func(x) {
return typeof(x)=="num" or !math.isnan(num(x));
}
var isscalar=func(s){
var isscalar = func(s) {
var t=typeof(s);
return (t=="num" or t=="str")?1:0;
return (t=="num" or t=="str")? 1:0;
}
var isstr=func(s){
var isstr = func(s) {
return typeof(s)=="str";
}
var isvec=func(v){
var isvec = func(v) {
return typeof(v)=="vec";
}
var ghosttype=func(ghost_object) {
var ghosttype = func(ghost_object) {
return __ghosttype(ghost_object);
}
# get the index of val in the vec
var vecindex=func(vec,val){
var vecindex = func(vec,val) {
forindex(var i;vec)
if(val==vec[i])
return i;
@@ -263,8 +263,8 @@ var vecindex=func(vec,val){
}
# check if the object is an instance of the class
var isa=func(object,class){
if(!contains(object,"parents") or typeof(object.parents)!="vec")
var isa = func(object, class) {
if(!contains(object, "parents") or typeof(object.parents)!="vec")
return 0;
foreach(var elem;object.parents)
if(elem==class)
@@ -273,202 +273,202 @@ var isa=func(object,class){
}
# assert aborts when condition is not true
var assert=func(condition,message="assertion failed!"){
var assert = func(condition, message = "assertion failed!") {
if(condition)
return 1;
die(message);
}
# get time stamp, this will return a timestamp object
var maketimestamp=func(){
var t=0;
var maketimestamp = func() {
var t = 0;
return {
stamp:func(){t=__millisec();},
elapsedMSec:func(){return __millisec()-t;},
elapsedUSec:func(){return (__millisec()-t)*1000;}
stamp: func() {t = __millisec();},
elapsedMSec: func() {return __millisec()-t;},
elapsedUSec: func() {return (__millisec()-t)*1000;}
};
}
# md5
var md5=func(str){
var md5 = func(str) {
return __md5(str);
}
var io={
SEEK_SET:0,
SEEK_CUR:1,
SEEK_END:2,
var io = {
SEEK_SET: 0,
SEEK_CUR: 1,
SEEK_END: 2,
# get content of a file by filename. returns a string.
readfile: func(filename){return __readfile(filename);},
readfile: func(filename) {return __readfile(filename);},
# input a string as the content of a file.
fout: func(filename,str){return __fout(filename,str);},
fout: func(filename, str) {return __fout(filename, str);},
# use C access
exists:func(filename){return __exists(filename);},
exists:func(filename) {return __exists(filename);},
# same as C fopen. open file and get the FILE*.
open: func(filename,mode="r"){return __open(filename,mode);},
open: func(filename, mode = "r") {return __open(filename, mode);},
# same as C fclose. close file by FILE*.
close: func(filehandle){return __close(filehandle);},
close: func(filehandle) {return __close(filehandle);},
# same as C fread. read file by FILE*.
# caution: buf must be a mutable string.use mut("") to get an empty mutable string.
read: func(filehandle,buf,len){return __read(filehandle,buf,len);},
read: func(filehandle, buf, len) {return __read(filehandle, buf, len);},
# same as C fwrite. write file by FILE*.
write: func(filehandle,str){return __write(filehandle,str);},
write: func(filehandle, str) {return __write(filehandle, str);},
# same as C fseek. seek place by FILE*.
seek: func(filehandle,pos,whence){return __seek(filehandle,pos,whence);},
seek: func(filehandle, pos, whence) {return __seek(filehandle, pos, whence);},
# same as C ftell.
tell: func(filehandle){return __tell(filehandle);},
tell: func(filehandle) {return __tell(filehandle);},
# read file by lines. use FILE*.
# get nil if EOF
readln:func(filehandle){return __readln(filehandle);},
readln: func(filehandle) {return __readln(filehandle);},
# same as C stat.
stat: func(filename){return __stat(filename);},
stat: func(filename) {return __stat(filename);},
# same as C feof. check if FILE* gets the end of file(EOF).
eof: func(filehandle){return __eof(filehandle);}
eof: func(filehandle) {return __eof(filehandle);}
};
# get file status. using data from io.stat
var fstat=func(filename){
var s=io.stat(filename);
var fstat = func(filename) {
var s = io.stat(filename);
return {
st_dev: s[0],
st_ino: s[1],
st_dev: s[0],
st_ino: s[1],
st_mode: s[2],
st_nlink:s[3],
st_uid: s[4],
st_gid: s[5],
st_nlink: s[3],
st_uid: s[4],
st_gid: s[5],
st_rdev: s[6],
st_size: s[7],
st_atime:s[8],
st_mtime:s[9],
st_ctime:s[10]
st_atime: s[8],
st_mtime: s[9],
st_ctime: s[10]
};
}
# functions that do bitwise calculation.
# carefully use it, all the calculations are based on integer.
var bits={
var bits = {
# u32 xor
u32_xor: func(a,b){return __u32xor(a,b); },
u32_xor: func(a, b) {return __u32xor(a, b);},
# u32 and
u32_and: func(a,b){return __u32and(a,b); },
u32_and: func(a, b) {return __u32and(a, b);},
# u32 or
u32_or: func(a,b){return __u32or(a,b); },
u32_or: func(a, b) {return __u32or(a, b);},
# u32 nand
u32_nand:func(a,b){return __u32nand(a,b);},
u32_nand: func(a, b) {return __u32nand(a, b);},
# u32 not
u32_not: func(a) {return __u32not(a); },
u32_not: func(a) {return __u32not(a);},
# get bit data from a special string. for example:
# bits.fld(s,0,3);
# if s stores 10100010(162)
# will get 101(5).
fld: func(str,startbit,len){return __fld;},
fld: func(str, startbit, len) {return __fld;},
# get sign-extended data from a special string. for example:
# bits.sfld(s,0,3);
# if s stores 10100010(162)
# will get 101(5) then this will be signed extended to
# 11111101(-3).
sfld: func(str,startbit,len){return __sfld;},
sfld: func(str, startbit, len) {return __sfld;},
# set value into a special string to store it. little-endian, for example:
# bits.setfld(s,0,8,69);
# set 01000101(69) to string will get this:
# 10100010(162)
# so s[0]=162.
setfld: func(str,startbit,len,val){return __setfld;},
setfld: func(str, startbit, len, val) {return __setfld;},
# get a special string filled by '\0' to use in setfld.
buf: func(len){return __buf;}
buf: func(len) {return __buf;}
};
# mostly used math functions and special constants, you know.
var math={
e: 2.7182818284590452354,
pi: 3.14159265358979323846264338327950288,
D2R: 2.7182818284590452354/180,
R2D: 180/2.7182818284590452354,
inf: 1/0,
nan: 0/0,
abs: func(x) {return x>0?x:-x; },
floor: func(x) {return __floor(x); },
pow: func(x,y){return __pow(x,y); },
sin: func(x) {return __sin(x); },
cos: func(x) {return __cos(x); },
tan: func(x) {return __tan(x); },
exp: func(x) {return __exp(x); },
lg: func(x) {return __lg(x); },
ln: func(x) {return __ln(x); },
sqrt: func(x) {return __sqrt(x); },
atan2: func(x,y){return __atan2(x,y);},
isnan: func(x) {return __isnan(x); },
max: func(x,y){return x>y?x:y; },
min: func(x,y){return x<y?x:y; }
var math = {
e: 2.7182818284590452354,
pi: 3.14159265358979323846264338327950288,
D2R: 2.7182818284590452354/180,
R2D: 180/2.7182818284590452354,
inf: 1/0,
nan: 0/0,
abs: func(x) {return x>0? x:-x;},
floor: func(x) {return __floor(x);},
pow: func(x, y) {return __pow(x, y);},
sin: func(x) {return __sin(x);},
cos: func(x) {return __cos(x);},
tan: func(x) {return __tan(x);},
exp: func(x) {return __exp(x);},
lg: func(x) {return __lg(x);},
ln: func(x) {return __ln(x);},
sqrt: func(x) {return __sqrt(x);},
atan2: func(x, y) {return __atan2(x, y);},
isnan: func(x) {return __isnan(x);},
max: func(x, y) {return x>y? x:y;},
min: func(x, y) {return x<y? x:y;}
};
var unix={
pipe: func(){return __pipe;},
fork: func(){return __fork;},
dup2: func(fd0,fd1){die("not supported yet");},
exec: func(filename,argv,envp){die("not supported yet");},
waitpid: func(pid,nohang=0){return __waitpid;},
isdir: func(path){return !!bits.u32_and(io.stat(path)[2],0x4000);}, # S_IFDIR 0x4000
isfile: func(path){return !!bits.u32_and(io.stat(path)[2],0x8000);}, # S_IFREG 0x8000
opendir: func(path){return __opendir;},
readdir: func(handle){return __readdir;},
closedir: func(handle){return __closedir;},
time: func(){return time(0);},
sleep: func(secs){return __sleep(secs);},
chdir: func(path){return __chdir(path);},
environ: func(){return __environ();},
getcwd: func(){return __getcwd();},
getenv: func(envvar){return __getenv(envvar);},
getpath: func(){return split(os.platform()=="windows"?";":":",unix.getenv("PATH"));}
var unix = {
pipe: func() {return __pipe;},
fork: func() {return __fork;},
dup2: func(fd0, fd1) {die("not supported yet");},
exec: func(filename, argv, envp) {die("not supported yet");},
waitpid: func(pid, nohang = 0) {return __waitpid;},
isdir: func(path) {return !!bits.u32_and(io.stat(path)[2],0x4000);}, # S_IFDIR 0x4000
isfile: func(path) {return !!bits.u32_and(io.stat(path)[2],0x8000);}, # S_IFREG 0x8000
opendir: func(path) {return __opendir;},
readdir: func(handle) {return __readdir;},
closedir: func(handle) {return __closedir;},
time: func() {return time(0);},
sleep: func(secs) {return __sleep(secs);},
chdir: func(path) {return __chdir(path);},
environ: func() {return __environ();},
getcwd: func() {return __getcwd();},
getenv: func(envvar) {return __getenv(envvar);},
getpath: func() {return split(os.platform()=="windows"? ";":":", unix.getenv("PATH"));}
};
# dylib is the core hashmap for developers to load their own library.
# for safe using dynamic library, you could use 'module' in stl/module.nas
var dylib={
var dylib = {
# open dynamic lib. return a hash including dl pointer and function pointers
dlopen: func(libname){
dlopen: func(libname) {
# find dynamic lib from local dir first
libname=(os.platform()=="windows"?".\\":"./")~libname;
libname = (os.platform()=="windows"? ".\\":"./")~libname;
if(io.exists(libname))
return __dlopen(libname);
# find dynamic lib through PATH
var envpath=split(os.platform()=="windows"?";":":",unix.getenv("PATH"));
var envpath = split(os.platform()=="windows"? ";":":",unix.getenv("PATH"));
# first find ./module
append(envpath,".");
var path=os.platform()=="windows"?"\\module\\":"/module/";
foreach(var p;envpath){
p~=path~libname;
if(io.exists(p)){
libname=p;
append(envpath, ".");
var path = os.platform()=="windows"? "\\module\\":"/module/";
foreach(var p;envpath) {
p ~= path~libname;
if(io.exists(p)) {
libname = p;
break;
}
}
return __dlopen(libname);
},
# close dynamic lib, this operation will make all the symbols loaded from it invalid.
dlclose: func(lib){return __dlclose; },
dlclose: func(lib) {return __dlclose;},
# call the loaded symbol, with infinite parameters:
# Caution: this may cause garbage collection process, be aware of the performance.
dlcall: func(ptr,args...){return __dlcallv},
dlcall: func(ptr, args...) {return __dlcallv},
# get dlcall function with limited parameter list
limitcall: func(arg_size=0){
if(arg_size==0){return func(ptr){return __dlcall};}
elsif(arg_size==1){return func(ptr,_0){return __dlcall};}
elsif(arg_size==2){return func(ptr,_0,_1){return __dlcall};}
elsif(arg_size==3){return func(ptr,_0,_1,_2){return __dlcall};}
elsif(arg_size==4){return func(ptr,_0,_1,_2,_3){return __dlcall};}
elsif(arg_size==5){return func(ptr,_0,_1,_2,_3,_4){return __dlcall};}
elsif(arg_size==6){return func(ptr,_0,_1,_2,_3,_4,_5){return __dlcall};}
elsif(arg_size==7){return func(ptr,_0,_1,_2,_3,_4,_5,_6){return __dlcall};}
elsif(arg_size==8){return func(ptr,_0,_1,_2,_3,_4,_5,_6,_7){return __dlcall};}
else{return func(ptr,args...){return __dlcallv};}
limitcall: func(arg_size = 0) {
if(arg_size==0) {return func(ptr) {return __dlcall};}
elsif(arg_size==1) {return func(ptr, _0) {return __dlcall};}
elsif(arg_size==2) {return func(ptr, _0, _1) {return __dlcall};}
elsif(arg_size==3) {return func(ptr, _0, _1, _2) {return __dlcall};}
elsif(arg_size==4) {return func(ptr, _0, _1, _2, _3) {return __dlcall};}
elsif(arg_size==5) {return func(ptr, _0, _1, _2, _3, _4) {return __dlcall};}
elsif(arg_size==6) {return func(ptr, _0, _1, _2, _3, _4, _5) {return __dlcall};}
elsif(arg_size==7) {return func(ptr, _0, _1, _2, _3, _4, _5, _6) {return __dlcall};}
elsif(arg_size==8) {return func(ptr, _0, _1, _2, _3, _4, _5, _6, _7) {return __dlcall};}
else {return func(ptr, args...) {return __dlcallv};}
}
};
# os is used to use or get some os-related info/functions.
# windows/macOS/linux are supported.
var os={
var os = {
# get a string that tell which os it runs on.
platform: func() {return __platform;},
time: func() {return __logtime;},
@@ -476,39 +476,39 @@ var os={
};
# runtime gives us some functions that we could manage it manually.
var runtime={
var runtime = {
# command line arguments
argv: func(){return __sysargv;},
argv: func() {return __sysargv;},
gc: {
extend: func(type){return __gcextd;}
extend: func(type) {return __gcextd;}
}
};
# functions that not supported in this runtime:
var bind=func(function,locals,outer_scope=nil){
var bind = func(function, locals, outer_scope = nil) {
die("this runtime does not support bind");
}
var call=func(function,args=nil,_me=nil,locals=nil,error=nil){
var call = func(function ,args = nil, _me = nil, locals = nil, error = nil) {
die("this runtime does not support call");
}
var caller=func(level=1){
var caller = func(level = 1) {
die("this runtime does not support caller");
}
var closure=func(function,level=1){
var closure = func(function, level = 1) {
die("this runtime uses \"vm_upval\" instead of \"vm_hash\" as the closure");
}
var compile=func(code,filename="<compile>"){
var compile = func(code, filename = "<compile>") {
die("this runtime uses static code generator");
}
var coroutine={
create: func(function) {return __cocreate;},
resume: func(co,args...) {return __coresume;},
yield: func(args...) {return __coyield; },
status: func(co) {return __costatus;},
running:func() {return __corun; }
var coroutine = {
create: func(function) {return __cocreate;},
resume: func(co, args...) {return __coresume;},
yield: func(args...) {return __coyield; },
status: func(co) {return __costatus;},
running:func() {return __corun; }
};

12
test/gc_test.nas Normal file
View File

@@ -0,0 +1,12 @@
var res = [];
for(var i=0;i<1e6;i+=1) {
append(res, []);
}
res = [];
for(var i=0;i<1e6;i+=1) {
append(res, {});
}
res = [];
for(var i=0;i<1e6;i+=1) {
append(res, func {});
}