optimize location info gen in codegen

and refactor lib.nas, add multiple native modules
This commit is contained in:
ValKmjolnir 2023-07-23 23:57:25 +08:00
parent 51a0ef6b0c
commit 788e0026c2
31 changed files with 477 additions and 356 deletions

64
doc/namespace.md Normal file
View File

@ -0,0 +1,64 @@
# Nasal Namespace
![feigenbaum](./pic/feigenbaum.png)
## Introduction
In this nasal interpreter,
we use this way below to construct namespaces:
- library is linked directly with the script
- module is wraped by a function generated by linker, and return a hash
## Library
Library file is linked with script file directly, like this:
In `std/lib.nas`:
```nasal
var a = 1;
```
In `example.nas`:
```nasal
var b = 1;
```
At the link stage,
in fact we put the ast of two files together to make a new ast,
so the result is equal to:
```nasal
var a = 1;
var b = 1;
```
## Module
Modules is wraped up by a function,
and return a hash, for example:
In `std/example_module.nas`:
```nasal
var a = 1;
```
We analysed this file and generated the ast.
Then we find all the global symbols.
At last we use the information of all the globals symbols in this file to generate a hash to return.
So the result is equal to:
```nasal
var example_module = func {
# source code begin
var a = 1;
# source code end
return {
a: a
};
}();
```

View File

@ -1,3 +1,4 @@
var libfib=func(){
var dl=dylib.dlopen("libfib."~(os.platform()=="windows"?"dll":"so"));
var fib=dl.fib;

View File

@ -2,13 +2,6 @@
#include <iostream>
bool ast_dumper::visit_file_info(file_info* node) {
dump_indent();
std::cout << "file \"" << node->get_file_name() << "\"";
std::cout << format_location(node->get_location());
return true;
}
bool ast_dumper::visit_null_expr(null_expr* node) {
dump_indent();
std::cout << "null" << format_location(node->get_location());

View File

@ -39,7 +39,6 @@ private:
}
public:
bool visit_file_info(file_info*) override;
bool visit_null_expr(null_expr*) override;
bool visit_nil_expr(nil_expr*) override;
bool visit_number_literal(number_literal*) override;

View File

@ -10,10 +10,6 @@ bool ast_visitor::visit_call(call* node) {
return true;
}
bool ast_visitor::visit_file_info(file_info* node) {
return true;
}
bool ast_visitor::visit_null_expr(null_expr* node) {
return true;
}

View File

@ -6,7 +6,6 @@ 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*);
virtual bool visit_number_literal(number_literal*);

View File

@ -9,10 +9,6 @@ void call::accept(ast_visitor* visitor) {
visitor->visit_call(this);
}
void file_info::accept(ast_visitor* visitor) {
visitor->visit_file_info(this);
}
void null_expr::accept(ast_visitor* visitor) {
visitor->visit_null_expr(this);
}

View File

@ -8,7 +8,6 @@
enum class expr_type:u32 {
ast_null = 0, // null node
ast_file_info, // stores file info
ast_block, // code block
ast_nil, // nil keyword
ast_num, // number, basic value type
@ -84,21 +83,6 @@ public:
virtual void accept(ast_visitor*);
};
class file_info:public expr {
private:
uint16_t index;
std::string filename;
public:
file_info(const span& location, uint16_t file_index, const std::string& name):
expr(location, expr_type::ast_file_info),
index(file_index), filename(name) {}
~file_info() = default;
uint16_t get_index() const {return index;}
const std::string& get_file_name() const {return filename;}
void accept(ast_visitor*) override;
};
class null_expr:public expr {
public:
null_expr(const span& location):

View File

@ -100,42 +100,44 @@ i32 codegen::upvalue_find(const std::string& name) {
return index;
}
void codegen::gen(u8 operation_code, u32 num, u32 line) {
code.push_back({operation_code, fileindex, num, line});
void codegen::gen(u8 operation_code, u32 num, const span& loc) {
code.push_back({operation_code,
static_cast<u16>(file_map.at(loc.file)),
num, loc.begin_line});
}
void codegen::num_gen(number_literal* node) {
f64 num = node->get_number();
regist_num(num);
gen(op_pnum,const_number_map.at(num), node->get_line());
gen(op_pnum, const_number_map.at(num), node->get_location());
}
void codegen::str_gen(string_literal* node) {
const auto& str = node->get_content();
regist_str(str);
gen(op_pstr, const_string_map.at(str), node->get_line());
gen(op_pstr, const_string_map.at(str), node->get_location());
}
void codegen::bool_gen(bool_literal* node) {
f64 num = node->get_flag()? 1:0;
regist_num(num);
gen(op_pnum, const_number_map.at(num), node->get_line());
gen(op_pnum, const_number_map.at(num), node->get_location());
}
void codegen::vec_gen(vector_expr* node) {
for(auto child : node->get_elements()) {
calc_gen(child);
}
gen(op_newv, node->get_elements().size(), node->get_line());
gen(op_newv, node->get_elements().size(), node->get_location());
}
void codegen::hash_gen(hash_expr* node) {
gen(op_newh, 0, node->get_line());
gen(op_newh, 0, node->get_location());
for(auto child : node->get_members()) {
calc_gen(child->get_value());
const auto& field_name = child->get_name();
regist_str(field_name);
gen(op_happ, const_string_map.at(field_name), child->get_line());
gen(op_happ, const_string_map.at(field_name), child->get_location());
}
}
@ -175,9 +177,9 @@ void codegen::func_gen(function* node) {
}
usize newf=code.size();
gen(op_newf, 0, node->get_line());
gen(op_newf, 0, node->get_location());
usize lsize=code.size();
gen(op_intl, 0, node->get_line());
gen(op_intl, 0, node->get_location());
// add special keyword 'me' into symbol table
// this symbol is only used in local scope(function's scope)
@ -196,14 +198,14 @@ void codegen::func_gen(function* node) {
regist_str(name);
switch(tmp->get_parameter_type()) {
case parameter::param_type::normal_parameter:
gen(op_para, const_string_map.at(name), tmp->get_line());
gen(op_para, const_string_map.at(name), tmp->get_location());
break;
case parameter::param_type::default_parameter:
calc_gen(tmp->get_default_value());
gen(op_deft, const_string_map.at(name), tmp->get_line());
gen(op_deft, const_string_map.at(name), tmp->get_location());
break;
case parameter::param_type::dynamic_parameter:
gen(op_dyn, const_string_map.at(name), tmp->get_line());
gen(op_dyn, const_string_map.at(name), tmp->get_location());
break;
}
add_symbol(name);
@ -211,7 +213,7 @@ void codegen::func_gen(function* node) {
code[newf].num = code.size()+1; // entry
usize jmp_ptr = code.size();
gen(op_jmp, 0, node->get_line());
gen(op_jmp, 0, node->get_location());
auto block = node->get_code_block();
// search symbols first, must use after loading parameters
@ -244,8 +246,8 @@ void codegen::func_gen(function* node) {
if (!block->get_expressions().size() ||
block->get_expressions().back()->get_type()!=expr_type::ast_ret) {
gen(op_pnil, 0, block->get_line());
gen(op_ret, 0, block->get_line());
gen(op_pnil, 0, block->get_location());
gen(op_ret, 0, block->get_location());
}
code[jmp_ptr].num = code.size();
}
@ -269,7 +271,7 @@ void codegen::call_id(identifier* node) {
const auto& name = node->get_name();
for(u32 i = 0; builtin[i].name; ++i) {
if (builtin[i].name==name) {
gen(op_callb, i, node->get_line());
gen(op_callb, i, node->get_location());
if (local.empty()) {
die("should warp native function in local scope",
node->get_location());
@ -279,15 +281,15 @@ void codegen::call_id(identifier* node) {
}
i32 index;
if ((index=local_find(name))>=0) {
gen(op_calll, index, node->get_line());
gen(op_calll, index, node->get_location());
return;
}
if ((index=upvalue_find(name))>=0) {
gen(op_upval, index, node->get_line());
gen(op_upval, index, node->get_location());
return;
}
if ((index=global_find(name))>=0) {
gen(op_callg, index, node->get_line());
gen(op_callg, index, node->get_location());
return;
}
die("undefined symbol \"" + name + "\"", node->get_location());
@ -295,7 +297,7 @@ void codegen::call_id(identifier* node) {
void codegen::call_hash_gen(call_hash* node) {
regist_str(node->get_field());
gen(op_callh, const_string_map.at(node->get_field()), node->get_line());
gen(op_callh, const_string_map.at(node->get_field()), node->get_location());
}
void codegen::call_vector_gen(call_vector* node) {
@ -303,39 +305,39 @@ void codegen::call_vector_gen(call_vector* node) {
if (node->get_slices().size()==1 &&
!node->get_slices()[0]->get_end()) {
calc_gen(node->get_slices()[0]->get_begin());
gen(op_callv, 0, node->get_slices()[0]->get_line());
gen(op_callv, 0, node->get_slices()[0]->get_location());
return;
}
gen(op_slcbeg, 0, node->get_line());
gen(op_slcbeg, 0, node->get_location());
for(auto tmp : node->get_slices()) {
if (!tmp->get_end()) {
calc_gen(tmp->get_begin());
gen(op_slc, 0, tmp->get_line());
gen(op_slc, 0, tmp->get_location());
} else {
calc_gen(tmp->get_begin());
calc_gen(tmp->get_end());
gen(op_slc2, 0, tmp->get_line());
gen(op_slc2, 0, tmp->get_location());
}
}
gen(op_slcend, 0, node->get_line());
gen(op_slcend, 0, node->get_location());
}
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());
gen(op_newh, 0, node->get_location());
for(auto child : node->get_argument()) {
calc_gen(((hash_pair*)child)->get_value());
const auto& field_name = ((hash_pair*)child)->get_name();
regist_str(field_name);
gen(op_happ, const_string_map.at(field_name), child->get_line());
gen(op_happ, const_string_map.at(field_name), child->get_location());
}
gen(op_callfh, 0, node->get_line());
gen(op_callfh, 0, node->get_location());
} else {
for(auto child : node->get_argument()) {
calc_gen(child);
}
gen(op_callfv, node->get_argument().size(), node->get_line());
gen(op_callfv, node->get_argument().size(), node->get_location());
}
}
@ -390,15 +392,15 @@ void codegen::mcall_id(identifier* node) {
}
i32 index;
if ((index=local_find(name))>=0) {
gen(op_mcalll, index, node->get_line());
gen(op_mcalll, index, node->get_location());
return;
}
if ((index=upvalue_find(name))>=0) {
gen(op_mupval, index, node->get_line());
gen(op_mupval, index, node->get_location());
return;
}
if ((index=global_find(name))>=0) {
gen(op_mcallg, index, node->get_line());
gen(op_mcallg, index, node->get_location());
return;
}
die("undefined symbol \"" + name + "\"", node->get_location());
@ -415,20 +417,20 @@ void codegen::mcall_vec(call_vector* node) {
return;
}
calc_gen(call->get_begin());
gen(op_mcallv, 0, node->get_line());
gen(op_mcallv, 0, node->get_location());
}
void codegen::mcall_hash(call_hash* node) {
regist_str(node->get_field());
gen(op_mcallh, const_string_map.at(node->get_field()), node->get_line());
gen(op_mcallh, const_string_map.at(node->get_field()), node->get_location());
}
void codegen::single_def(definition_expr* node) {
const auto& str = node->get_variable_name()->get_name();
calc_gen(node->get_value());
local.empty()?
gen(op_loadg, global_find(str), node->get_line()):
gen(op_loadl, local_find(str), node->get_line());
gen(op_loadg, global_find(str), node->get_location()):
gen(op_loadl, local_find(str), node->get_location());
}
void codegen::multi_def(definition_expr* node) {
@ -448,21 +450,21 @@ void codegen::multi_def(definition_expr* node) {
calc_gen(vals[i]);
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_loadg, global_find(name), identifiers[i]->get_location()):
gen(op_loadl, local_find(name), identifiers[i]->get_location());
}
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());
gen(op_callvi, i, node->get_value()->get_location());
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_loadg, global_find(name), identifiers[i]->get_location()):
gen(op_loadl, local_find(name), identifiers[i]->get_location());
}
gen(op_pop, 0, node->get_line());
gen(op_pop, 0, node->get_location());
}
void codegen::def_gen(definition_expr* node) {
@ -477,7 +479,7 @@ void codegen::assignment_expression(assignment_expr* node) {
case assignment_expr::assign_type::equal:
calc_gen(node->get_right());
mcall(node->get_left());
gen(op_meq, 0, node->get_line());
gen(op_meq, 0, node->get_location());
break;
case assignment_expr::assign_type::add_equal:
if (node->get_right()->get_type()!=expr_type::ast_num) {
@ -485,11 +487,11 @@ void codegen::assignment_expression(assignment_expr* node) {
}
mcall(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
gen(op_addeq, 0, node->get_line());
gen(op_addeq, 0, node->get_location());
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_addeqc, const_number_map[num], node->get_line());
gen(op_addeqc, const_number_map[num], node->get_location());
}
break;
case assignment_expr::assign_type::sub_equal:
@ -498,11 +500,11 @@ void codegen::assignment_expression(assignment_expr* node) {
}
mcall(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
gen(op_subeq, 0, node->get_line());
gen(op_subeq, 0, node->get_location());
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_subeqc, const_number_map[num], node->get_line());
gen(op_subeqc, const_number_map[num], node->get_location());
}
break;
case assignment_expr::assign_type::mult_equal:
@ -511,11 +513,11 @@ void codegen::assignment_expression(assignment_expr* node) {
}
mcall(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
gen(op_muleq, 0, node->get_line());
gen(op_muleq, 0, node->get_location());
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_muleqc, const_number_map[num], node->get_line());
gen(op_muleqc, const_number_map[num], node->get_location());
}
break;
case assignment_expr::assign_type::div_equal:
@ -524,11 +526,11 @@ void codegen::assignment_expression(assignment_expr* node) {
}
mcall(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
gen(op_diveq, 0, node->get_line());
gen(op_diveq, 0, node->get_location());
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_diveqc, const_number_map[num], node->get_line());
gen(op_diveqc, const_number_map[num], node->get_location());
}
break;
case assignment_expr::assign_type::concat_equal:
@ -537,27 +539,27 @@ void codegen::assignment_expression(assignment_expr* node) {
}
mcall(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_str) {
gen(op_lnkeq, 0, node->get_line());
gen(op_lnkeq, 0, node->get_location());
} else {
const auto& str = ((string_literal*)node->get_right())->get_content();
regist_str(str);
gen(op_lnkeqc, const_string_map[str], node->get_line());
gen(op_lnkeqc, const_string_map[str], node->get_location());
}
break;
case assignment_expr::assign_type::bitwise_and_equal:
calc_gen(node->get_right());
mcall(node->get_left());
gen(op_btandeq, 0, node->get_line());
gen(op_btandeq, 0, node->get_location());
break;
case assignment_expr::assign_type::bitwise_or_equal:
calc_gen(node->get_right());
mcall(node->get_left());
gen(op_btoreq, 0, node->get_line());
gen(op_btoreq, 0, node->get_location());
break;
case assignment_expr::assign_type::bitwise_xor_equal:
calc_gen(node->get_right());
mcall(node->get_left());
gen(op_btxoreq, 0, node->get_line());
gen(op_btxoreq, 0, node->get_location());
break;
}
}
@ -581,7 +583,7 @@ void codegen::assignment_statement(assignment_expr* node) {
if (code.back().op==op_meq) {
code.back().num=1;
} else {
gen(op_pop, 0, node->get_line());
gen(op_pop, 0, node->get_location());
}
}
break;
@ -599,7 +601,7 @@ void codegen::assignment_statement(assignment_expr* node) {
} else if (op_addeqc<=code.back().op && code.back().op<=op_lnkeqc) {
code.back().op=code.back().op-op_addeqc+op_addecp;
} else {
gen(op_pop, 0, node->get_line());
gen(op_pop, 0, node->get_location());
}
break;
}
@ -630,7 +632,7 @@ void codegen::multi_assign_gen(multi_assign* node) {
} else if (code.back().op==op_mcallg) {
code.back().op=op_loadg;
} else {
gen(op_meq, 1, tuple[i]->get_line());
gen(op_meq, 1, tuple[i]->get_location());
}
}
return;
@ -638,7 +640,7 @@ void codegen::multi_assign_gen(multi_assign* node) {
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());
gen(op_callvi, i, node->get_value()->get_location());
// multi assign user loadl and loadg to avoid meq's stack--
// and this operation changes local and global value directly
mcall(tuple[i]);
@ -649,35 +651,35 @@ void codegen::multi_assign_gen(multi_assign* node) {
} else if (code.back().op==op_mcallg) {
code.back().op=op_loadg;
} else {
gen(op_meq, 1, tuple[i]->get_line());
gen(op_meq, 1, tuple[i]->get_location());
}
}
gen(op_pop, 0, node->get_line());
gen(op_pop, 0, node->get_location());
}
void codegen::cond_gen(condition_expr* node) {
std::vector<usize> jmp_label;
calc_gen(node->get_if_statement()->get_condition());
auto ptr = code.size();
gen(op_jf, 0, node->get_if_statement()->get_line());
gen(op_jf, 0, node->get_if_statement()->get_location());
block_gen(node->get_if_statement()->get_code_block());
if (node->get_elsif_stataments().size() ||
node->get_else_statement()) {
jmp_label.push_back(code.size());
gen(op_jmp, 0, node->get_if_statement()->get_line());
gen(op_jmp, 0, node->get_if_statement()->get_location());
}
code[ptr].num = code.size();
for(auto tmp : node->get_elsif_stataments()) {
calc_gen(tmp->get_condition());
ptr = code.size();
gen(op_jf, 0, tmp->get_line());
gen(op_jf, 0, tmp->get_location());
block_gen(tmp->get_code_block());
// the last condition doesn't need to jmp
if (tmp!=node->get_elsif_stataments().back() ||
node->get_else_statement()) {
jmp_label.push_back(code.size());
gen(op_jmp, 0, tmp->get_line());
gen(op_jmp, 0, tmp->get_location());
}
code[ptr].num=code.size();
}
@ -719,10 +721,10 @@ void codegen::while_gen(while_expr* node) {
usize loop_ptr = code.size();
calc_gen(node->get_condition());
usize condition_ptr = code.size();
gen(op_jf, 0, node->get_condition()->get_line());
gen(op_jf, 0, node->get_condition()->get_location());
block_gen(node->get_code_block());
gen(op_jmp, loop_ptr, node->get_code_block()->get_line());
gen(op_jmp, loop_ptr, node->get_code_block()->get_location());
code[condition_ptr].num = code.size();
load_continue_break(code.size()-1, code.size());
}
@ -732,17 +734,17 @@ 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, const_number_map.at(1), node->get_condition()->get_line());
gen(op_pnum, const_number_map.at(1), node->get_condition()->get_location());
} else {
calc_gen(node->get_condition());
}
usize label_exit = code.size();
gen(op_jf, 0, node->get_condition()->get_line());
gen(op_jf, 0, node->get_condition()->get_location());
block_gen(node->get_code_block());
usize continue_place = code.size();
statement_generation(node->get_step());
gen(op_jmp, jmp_place, node->get_step()->get_line());
gen(op_jmp, jmp_place, node->get_step()->get_location());
code[label_exit].num = code.size();
load_continue_break(continue_place, code.size());
@ -750,19 +752,19 @@ void codegen::for_gen(for_expr* node) {
void codegen::forei_gen(forei_expr* node) {
calc_gen(node->get_value());
gen(op_cnt, 0, node->get_value()->get_line());
gen(op_cnt, 0, node->get_value()->get_location());
usize ptr = code.size();
if (node->get_loop_type()==forei_expr::forei_loop_type::forindex) {
gen(op_findex, 0, node->get_line());
gen(op_findex, 0, node->get_location());
} else {
gen(op_feach, 0, node->get_line());
gen(op_feach, 0, node->get_location());
}
if (node->get_iterator()->get_name()) { // define a new iterator
auto name_node = node->get_iterator()->get_name();
const auto& str = name_node->get_name();
local.empty()?
gen(op_loadg, global_find(str), name_node->get_line()):
gen(op_loadl, local_find(str), name_node->get_line());
gen(op_loadg, global_find(str), name_node->get_location()):
gen(op_loadl, local_find(str), name_node->get_location());
} else { // use exist variable as the iterator
mcall(node->get_iterator()->get_call());
if (code.back().op==op_mcallg) {
@ -772,17 +774,17 @@ void codegen::forei_gen(forei_expr* node) {
} else if (code.back().op==op_mupval) {
code.back().op=op_loadu;
} else {
gen(op_meq, 1, node->get_iterator()->get_line());
gen(op_meq, 1, node->get_iterator()->get_location());
}
}
++in_loop_level.back();
block_gen(node->get_code_block());
--in_loop_level.back();
gen(op_jmp, ptr, node->get_line());
gen(op_jmp, ptr, node->get_location());
code[ptr].num=code.size();
load_continue_break(code.size()-1, code.size());
gen(op_pop, 0, node->get_value()->get_line());// pop vector
gen(op_pop, 0, node->get_line());// pop iterator
gen(op_pop, 0, node->get_value()->get_location());// pop vector
gen(op_pop, 0, node->get_location());// pop iterator
}
void codegen::statement_generation(expr* node) {
@ -806,7 +808,7 @@ void codegen::statement_generation(expr* node) {
case expr_type::ast_binary:
case expr_type::ast_ternary:
calc_gen(node);
gen(op_pop, 0, node->get_line());
gen(op_pop, 0, node->get_location());
break;
default: break;
}
@ -815,33 +817,33 @@ void codegen::statement_generation(expr* node) {
void codegen::or_gen(binary_operator* node) {
calc_gen(node->get_left());
usize label_jump_true_1 = code.size();
gen(op_jt, 0, node->get_left()->get_line());
gen(op_jt, 0, node->get_left()->get_location());
gen(op_pop, 0, node->get_left()->get_line());
gen(op_pop, 0, node->get_left()->get_location());
calc_gen(node->get_right());
usize label_jump_true_2 = code.size();
gen(op_jt, 0, node->get_right()->get_line());
gen(op_jt, 0, node->get_right()->get_location());
gen(op_pop, 0, node->get_right()->get_line());
gen(op_pnil, 0, node->get_right()->get_line());
gen(op_pop, 0, node->get_right()->get_location());
gen(op_pnil, 0, node->get_right()->get_location());
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());
gen(op_jt, code.size()+2, node->get_left()->get_location());
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_jmp, 0, node->get_left()->get_location());
gen(op_pop, 0, node->get_right()->get_location()); // jt jumps here
calc_gen(node->get_right());
gen(op_jt, code.size()+3, node->get_right()->get_line());
gen(op_jt, code.size()+3, node->get_right()->get_location());
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());
gen(op_pop, 0, node->get_right()->get_location());
gen(op_pnil, 0, node->get_right()->get_location());
// jt jumps here, avoid pop and pnil
}
@ -849,11 +851,11 @@ void codegen::unary_gen(unary_operator* node) {
calc_gen(node->get_value());
switch(node->get_operator_type()) {
case unary_operator::unary_type::negative:
gen(op_usub, 0, node->get_line()); break;
gen(op_usub, 0, node->get_location()); break;
case unary_operator::unary_type::logical_not:
gen(op_lnot, 0, node->get_line()); break;
gen(op_lnot, 0, node->get_location()); break;
case unary_operator::unary_type::bitwise_not:
gen(op_bnot, 0, node->get_line()); break;
gen(op_bnot, 0, node->get_location()); break;
}
}
@ -877,27 +879,27 @@ void codegen::binary_gen(binary_operator* node) {
case binary_operator::binary_type::cmpeq:
calc_gen(node->get_left());
calc_gen(node->get_right());
gen(op_eq, 0, node->get_line());
gen(op_eq, 0, node->get_location());
return;
case binary_operator::binary_type::cmpneq:
calc_gen(node->get_left());
calc_gen(node->get_right());
gen(op_neq, 0, node->get_line());
gen(op_neq, 0, node->get_location());
return;
case binary_operator::binary_type::bitwise_or:
calc_gen(node->get_left());
calc_gen(node->get_right());
gen(op_btor, 0, node->get_line());
gen(op_btor, 0, node->get_location());
return;
case binary_operator::binary_type::bitwise_xor:
calc_gen(node->get_left());
calc_gen(node->get_right());
gen(op_btxor, 0, node->get_line());
gen(op_btxor, 0, node->get_location());
return;
case binary_operator::binary_type::bitwise_and:
calc_gen(node->get_left());
calc_gen(node->get_right());
gen(op_btand, 0, node->get_line());
gen(op_btand, 0, node->get_location());
return;
default: break;
}
@ -906,99 +908,99 @@ void codegen::binary_gen(binary_operator* node) {
calc_gen(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
gen(op_add, 0, node->get_line());
gen(op_add, 0, node->get_location());
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_addc, const_number_map.at(num), node->get_line());
gen(op_addc, const_number_map.at(num), node->get_location());
}
return;
case binary_operator::binary_type::sub:
calc_gen(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
gen(op_sub, 0, node->get_line());
gen(op_sub, 0, node->get_location());
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_subc, const_number_map.at(num), node->get_line());
gen(op_subc, const_number_map.at(num), node->get_location());
}
return;
case binary_operator::binary_type::mult:
calc_gen(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
gen(op_mul, 0, node->get_line());
gen(op_mul, 0, node->get_location());
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_mulc, const_number_map.at(num), node->get_line());
gen(op_mulc, const_number_map.at(num), node->get_location());
}
return;
case binary_operator::binary_type::div:
calc_gen(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
gen(op_div, 0, node->get_line());
gen(op_div, 0, node->get_location());
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_divc, const_number_map.at(num), node->get_line());
gen(op_divc, const_number_map.at(num), node->get_location());
}
return;
case binary_operator::binary_type::concat:
calc_gen(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_str) {
calc_gen(node->get_right());
gen(op_lnk, 0, node->get_line());
gen(op_lnk, 0, node->get_location());
} else {
const auto& str = ((string_literal*)node->get_right())->get_content();
regist_str(str);
gen(op_lnkc, const_string_map.at(str), node->get_line());
gen(op_lnkc, const_string_map.at(str), node->get_location());
}
break;
case binary_operator::binary_type::less:
calc_gen(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
gen(op_less, 0, node->get_line());
gen(op_less, 0, node->get_location());
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_lessc, const_number_map.at(num), node->get_line());
gen(op_lessc, const_number_map.at(num), node->get_location());
}
return;
case binary_operator::binary_type::leq:
calc_gen(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
gen(op_leq, 0, node->get_line());
gen(op_leq, 0, node->get_location());
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_leqc, const_number_map.at(num), node->get_line());
gen(op_leqc, const_number_map.at(num), node->get_location());
}
return;
case binary_operator::binary_type::grt:
calc_gen(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
gen(op_grt, 0, node->get_line());
gen(op_grt, 0, node->get_location());
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_grtc, const_number_map.at(num), node->get_line());
gen(op_grtc, const_number_map.at(num), node->get_location());
}
return;
case binary_operator::binary_type::geq:
calc_gen(node->get_left());
if (node->get_right()->get_type()!=expr_type::ast_num) {
calc_gen(node->get_right());
gen(op_geq, 0, node->get_line());
gen(op_geq, 0, node->get_location());
} else {
auto num = ((number_literal*)node->get_right())->get_number();
regist_num(num);
gen(op_geqc, const_number_map.at(num), node->get_line());
gen(op_geqc, const_number_map.at(num), node->get_location());
}
return;
default: break;
@ -1008,10 +1010,10 @@ void codegen::binary_gen(binary_operator* node) {
void codegen::trino_gen(ternary_operator* node) {
calc_gen(node->get_condition());
usize label_jump_false = code.size();
gen(op_jf, 0, node->get_condition()->get_line());
gen(op_jf, 0, node->get_condition()->get_location());
calc_gen(node->get_left());
usize label_jump_to_exit = code.size();
gen(op_jmp, 0, node->get_left()->get_line());
gen(op_jmp, 0, node->get_left()->get_location());
code[label_jump_false].num = code.size();
calc_gen(node->get_right());
code[label_jump_to_exit].num = code.size();
@ -1020,7 +1022,7 @@ void codegen::trino_gen(ternary_operator* node) {
void codegen::calc_gen(expr* node) {
switch(node->get_type()) {
case expr_type::ast_nil:
gen(op_pnil, 0, node->get_line()); break;
gen(op_pnil, 0, node->get_location()); break;
case expr_type::ast_num:
num_gen((number_literal*)node); break;
case expr_type::ast_str:
@ -1064,18 +1066,15 @@ void codegen::block_gen(code_block* node) {
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;
case expr_type::ast_cond:
cond_gen((condition_expr*)tmp); break;
case expr_type::ast_continue:
continue_ptr.front().push_back(code.size());
gen(op_jmp, 0, tmp->get_line());
gen(op_jmp, 0, tmp->get_location());
break;
case expr_type::ast_break:
break_ptr.front().push_back(code.size());
gen(op_jmp, 0, tmp->get_line());
gen(op_jmp, 0, tmp->get_location());
break;
case expr_type::ast_while:
case expr_type::ast_for:
@ -1101,16 +1100,20 @@ void codegen::block_gen(code_block* node) {
void codegen::ret_gen(return_expr* node) {
for(u32 i = 0; i<in_loop_level.back(); ++i) {
gen(op_pop, 0, node->get_line());
gen(op_pop, 0, node->get_line());
gen(op_pop, 0, node->get_location());
gen(op_pop, 0, node->get_location());
}
calc_gen(node->get_value());
gen(op_ret, 0, node->get_line());
gen(op_ret, 0, node->get_location());
}
const error& codegen::compile(parse& parse, linker& import) {
fileindex = 0;
file = import.filelist();
const auto& file = import.filelist();
file_map = {};
for(usize i = 0; i<file.size(); ++i) {
file_map[file[i]] = i;
}
in_loop_level.push_back(0);
// add special symbol globals, which is a hash stores all global variables
@ -1119,9 +1122,9 @@ const error& codegen::compile(parse& parse, linker& import) {
add_symbol("arg");
find_symbol(parse.tree()); // search symbols first
gen(op_intg, global.size(), 0);
gen(op_intg, global.size(), parse.tree()->get_location());
block_gen(parse.tree()); // generate main block
gen(op_exit, 0, 0);
gen(op_exit, 0, parse.tree()->get_location());
// size out of bound check
if (const_number_table.size()>0xffffff) {

View File

@ -22,15 +22,24 @@
class codegen {
private:
u16 fileindex;
error err;
std::vector<std::string> file;
// file mapper for file -> index
std::unordered_map<std::string, usize> file_map;
// used for generate pop in return expression
std::vector<u32> in_loop_level;
// constant numbers and strings
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;
// generated opcodes
std::vector<opcode> code;
// used to store jmp operands index, to fill the jump address back
std::list<std::vector<i32>> continue_ptr;
std::list<std::vector<i32>> break_ptr;
@ -38,6 +47,7 @@ private:
// global : max STACK_DEPTH-1 values
std::unordered_map<std::string, i32> global;
std::unordered_map<std::string, std::unordered_set<std::string>> experimental_namespace;
// local : max 32768 upvalues 65536 values
// but in fact local scope also has less than STACK_DEPTH value
std::list<std::unordered_map<std::string, i32>> local;
@ -56,7 +66,7 @@ private:
i32 global_find(const std::string&);
i32 upvalue_find(const std::string&);
void gen(u8, u32, u32);
void gen(u8, u32, const span&);
void num_gen(number_literal*);
void str_gen(string_literal*);
@ -105,7 +115,7 @@ public:
}
public:
codegen(): fileindex(0) {}
codegen() = default;
const error& compile(parse&, linker&);
void print(std::ostream&);
void symbol_dump(std::ostream&) const;

View File

@ -313,12 +313,15 @@ definition_expr* linker::generate_module_definition(code_block* block) {
code_block* linker::load(code_block* root, u16 fileindex) {
auto tree = new code_block({0, 0, 0, 0, files[fileindex]});
// load library, this ast will be linked with root directly
// so no namespace is generated
if (!lib_loaded) {
auto tmp = import_nasal_lib();
link(tree, tmp);
delete tmp;
lib_loaded = true;
}
// load imported modules
for(auto i : root->get_expressions()) {
if (!import_check(i)) {
break;
@ -327,9 +330,6 @@ code_block* linker::load(code_block* root, u16 fileindex) {
tree->add_expression(generate_module_definition(tmp));
}
// add root to the back of tree
auto file_head = new file_info(
{0, 0, 0, 0, files[fileindex]}, fileindex, files[fileindex]);
tree->add_expression(file_head);
link(tree, root);
return tree;
}

View File

@ -1,5 +1,6 @@
# flightgear developer environments simulator (beta)
# ValKmjolnir 2022
import.std.runtime;
println("-------------------------------------------------------------");
println(" FlightGear simulated-env for developers project, since 2019");

68
std/io.nas Normal file
View File

@ -0,0 +1,68 @@
# io.nas
# 2023 by ValKmjolnir
var SEEK_SET = 0;
var SEEK_CUR = 1;
var SEEK_END = 2;
# get content of a file by filename. returns a string.
var readfile = func(filename) {
return __readfile(filename);
}
# input a string as the content of a file.
var fout = func(filename, str) {
return __fout(filename, str);
}
# use C access
var exists = func(filename) {
return __exists(filename);
}
# same as C fopen. open file and get the FILE*.
var open = func(filename, mode = "r") {
return __open(filename, mode);
}
# same as C fclose. close file by FILE*.
var 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.
var read = func(filehandle, buf, len) {
return __read(filehandle, buf, len);
}
# same as C fwrite. write file by FILE*.
var write = func(filehandle, str) {
return __write(filehandle, str);
}
# same as C fseek. seek place by FILE*.
var seek = func(filehandle, pos, whence) {
return __seek(filehandle, pos, whence);
}
# same as C ftell.
var tell = func(filehandle) {
return __tell(filehandle);
}
# read file by lines. use FILE*.
# get nil if EOF
var readln = func(filehandle) {
return __readln(filehandle);
}
# same as C stat.
var stat = func(filename) {
return __stat(filename);
}
# same as C feof. check if FILE* gets the end of file(EOF).
var eof = func(filehandle) {
return __eof(filehandle);
}

View File

@ -2,6 +2,10 @@
# 2019 ValKmjolnir
import.std.coroutine;
import.std.math;
import.std.string;
import.std.io;
import.std.os;
# print is used to print all things in nasal, try and see how it works.
# this function uses std::cout to output logs.
@ -327,38 +331,6 @@ var md5 = func(str) {
return __md5(str);
}
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);},
# input a string as the content of a file.
fout: func(filename, str) {return __fout(filename, str);},
# use C access
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);},
# same as C fclose. close file by FILE*.
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);},
# same as C fwrite. write file by FILE*.
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);},
# same as C ftell.
tell: func(filehandle) {return __tell(filehandle);},
# read file by lines. use FILE*.
# get nil if EOF
readln: func(filehandle) {return __readln(filehandle);},
# same as C stat.
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);}
};
# get file status. using data from io.stat
var fstat = func(filename) {
var s = io.stat(filename);
@ -412,28 +384,6 @@ var bits = {
buf: func(len) {return __buf;}
};
# mostly used math functions and special constants, you know.
var math = {
e: 2.7182818284590452354,
pi: 3.14159265358979323846264338327950288,
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;}
};
# important global constants
var D2R = math.pi / 180; # degree to radian
var R2D = 180 / math.pi; # radian to degree
@ -520,25 +470,6 @@ var dylib = {
}
};
# os is used to use or get some os-related info/functions.
# windows/macOS/linux are supported.
var os = {
# get a string that tell which os it runs on.
platform: func() {return __platform;},
time: func() {return __logtime;},
arch: func() {return __arch;}
};
# runtime gives us some functions that we could manage it manually.
var runtime = {
# command line arguments
argv: func() {return globals.arg;},
gc: {
extend: func(type) {return __gcextd;},
info: func() {return __gcinfo;}
}
};
# functions that not supported in this runtime:
var bind = func(function, locals, outer_scope = nil) {
die("this runtime does not support bind");

View File

@ -1,51 +1,77 @@
# list.nas
# valkmjolnir 2021/3/31
var list=func(){
var (begin,end)=(nil,nil);
return{
push_back:func(elem){
var tmp={elem:elem,prev:nil,next:nil};
if(end!=nil){
end.next=tmp;
tmp.prev=end;
end=tmp;
var new = func() {
var (begin, end, len) = (nil, nil, 0);
return {
push_back: func(elem) {
var tmp = {
elem: elem,
prev: nil,
next: nil
};
if(end!=nil) {
end.next = tmp;
tmp.prev = end;
end = tmp;
} else {
begin = end = tmp;
}
else
begin=end=tmp;
len += 1;
},
push_front:func(elem){
var tmp={elem:elem,prev:nil,next:nil};
if(begin!=nil){
begin.prev=tmp;
tmp.next=begin;
begin=tmp;
push_front: func(elem) {
var tmp = {
elem: elem,
prev: nil,
next: nil
};
if (begin!=nil) {
begin.prev = tmp;
tmp.next = begin;
begin = tmp;
} else {
begin = end = tmp;
}
else
begin=end=tmp;
len += 1;
},
pop_back:func(){
if(end!=nil)
end=end.prev;
if(end==nil)
begin=nil;
else
end.next=nil;
pop_back: func() {
if (end!=nil) {
end = end.prev;
}
if (end==nil) {
begin = nil;
} else {
end.next = nil;
}
if (len) {
len -= 1;
}
},
pop_front:func(){
if(begin!=nil)
begin=begin.next;
if(begin==nil)
end=nil;
else
begin.prev=nil;
pop_front: func() {
if (begin!=nil) {
begin = begin.next;
}
if (begin==nil) {
end = nil;
} else {
begin.prev = nil;
}
if (len) {
len -= 1;
}
},
front:func(){
if(begin!=nil)
front: func() {
if (begin!=nil) {
return begin.elem;
}
},
back:func(){
if(end!=nil)
back: func() {
if (end!=nil) {
return end.elem;
}
},
length: func() {
return len;
}
};
}

65
std/math.nas Normal file
View File

@ -0,0 +1,65 @@
# math.nas
# 2023 by ValKmjolnir
# mostly used math functions and special constants, you know.
var e = 2.7182818284590452354;
var pi = 3.14159265358979323846264338327950288;
var inf = 1/0;
var nan = 0/0;
var abs = func(x) {
return x>0? x:-x;
}
var floor = func(x) {
return __floor(x);
}
var pow = func(x, y) {
return __pow(x, y);
}
var sin = func(x) {
return __sin(x);
}
var cos = func(x) {
return __cos(x);
}
var tan = func(x) {
return __tan(x);
}
var exp = func(x) {
return __exp(x);
}
var lg = func(x) {
return __lg(x);
}
var ln = func(x) {
return __ln(x);
}
var sqrt = func(x) {
return __sqrt(x);
}
var atan2 = func(x, y) {
return __atan2(x, y);
}
var isnan = func(x) {
return __isnan(x);
}
var max = func(x, y) {
return x>y? x:y;
}
var min = func(x, y) {
return x<y? x:y;
}

18
std/os.nas Normal file
View File

@ -0,0 +1,18 @@
# os.nas
# 2023 by ValKmjolnir
# os is used to use or get some os-related info/functions.
# windows/macOS/linux are supported.
# get a string that tell which os it runs on.
var platform = func() {
return __platform;
}
var time = func() {
return __logtime;
}
var arch = func() {
return __arch;
}

13
std/runtime.nas Normal file
View File

@ -0,0 +1,13 @@
# runtime.nas
# 2023 by ValKmjolnir
# runtime gives us some functions that we could manage it manually.
# command line arguments
var argv = func() {
return globals.arg;
}
var gc = {
extend: func(type) {return __gcextd;},
info: func() {return __gcinfo;}
};

View File

@ -1,9 +1,12 @@
# string.nas
# ValKmjolnir 2022/10/5
var join=func(vec){
var res="";
foreach(var i;vec)
res~=i;
var join=func(sep, vec){
var len = size(vec);
var res = "";
for(var i = 0; i<len; i+=1) {
res ~= vec[i];
res ~= (i==len-1? "":sep);
}
return res;
}

View File

@ -22,6 +22,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import.module.libmat;
import.std.runtime;
func(){
# allocate more spaces

View File

@ -1,3 +1,5 @@
import.std.runtime;
var mod=func(n,a){
return n-int(n/a)*a;
}

View File

@ -1,3 +1,4 @@
import.std.runtime;
var test_func = func(test_processes...) {
var test_process_total = maketimestamp();

View File

@ -1,6 +1,7 @@
# hexdump.nas by ValKmjolnir
# 2021/8/13
import.std.file;
import.std.runtime;
# init
var hex=func(){

View File

@ -1,5 +1,6 @@
import.module.libsock;
import.std.json;
import.std.runtime;
var JSON = json.JSON;
var socket = libsock.socket;

View File

@ -1,4 +1,5 @@
import.std.process_bar;
import.std.runtime;
var new_map=func(width,height){
var tmp=[];

View File

@ -1,5 +1,6 @@
import.std.process_bar;
import.module.libkey;
import.std.runtime;
var is_windows_platform=os.platform()=="windows";
var is_macos_platform=os.platform()=="macOS";

View File

@ -1,3 +1,5 @@
import.std.runtime;
# basic type
nil;
2147483647;
@ -202,6 +204,7 @@ foreach(i;a){
;
}
println(runtime.argv());
println(globals.arg);
func(a,b,c,d="只有红茶可以吗"){
println(a,' ',b,' ',c,' ',d,' true: ',true,' false: ',false);
}(c:1919810,b:514,a:114);

View File

@ -1,71 +1,6 @@
import.module.libkey;
var list=func(){
var (begin,end,len)=(nil,nil,0);
return{
push_back:func(elem){
var tmp={
elem:elem,
prev:nil,
next:nil
};
if(end!=nil){
end.next=tmp;
tmp.prev=end;
end=tmp;
}else{
begin=end=tmp;
}
len+=1;
},
push_front:func(elem){
var tmp={
elem:elem,
prev:nil,
next:nil
};
if(begin!=nil){
begin.prev=tmp;
tmp.next=begin;
begin=tmp;
}else{
begin=end=tmp;
}
len+=1;
},
pop_back:func(){
if(end!=nil)
end=end.prev;
if(end==nil)
begin=nil;
else
end.next=nil;
if(len)
len-=1;
},
pop_front:func(){
if(begin!=nil)
begin=begin.next;
if(begin==nil)
end=nil;
else
begin.prev=nil;
if(len)
len-=1;
},
front:func(){
if(begin!=nil)
return begin.elem;
},
back:func(){
if(end!=nil)
return end.elem;
},
length:func(){
return len;
}
};
}
import.std.list;
import.std.runtime;
var game=func(x,y){
rand(time(0));
@ -88,7 +23,7 @@ var game=func(x,y){
vec[i][j]=0;
}
var snake=list();
var snake = list.new();
snake.push_back([int(x/2),int(y/3)]);
snake.push_back([int(x/2),int(y/3)+1]);
vec[int(x/2)][int(y/3)]=1;

View File

@ -1,4 +1,6 @@
import.module.libkey;
import.std.runtime;
var color=[
"\e[31m","\e[32m","\e[33m","\e[34m","\e[35m","\e[36m",
"\e[91m","\e[92m","\e[93m","\e[94m","\e[95m","\e[96m",

View File

@ -1,3 +1,5 @@
import.std.runtime;
var os_time=func(){
return "[\e[33;1m"~os.time()~"\e[0m] ";
}

View File

@ -1,3 +1,4 @@
import.std.runtime;
var to_lower=func(s){
var tmp="";