Merge pull request #24 from ValKmjolnir/develop

🐛 fix bug of argument "arg"
This commit is contained in:
Li Haokun 2023-07-11 19:13:42 +08:00 committed by GitHub
commit ea33a5ed81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 618 additions and 492 deletions

View File

@ -761,6 +761,8 @@ If get this, Congratulations!
<details><summary>Must use `var` to define variables</summary>
This interpreter uses more strict syntax to make sure it is easier for you to program and debug.
And flightgear's nasal interpreter also has the same rule.
So do not use variable without using `var` to declare it.
In Andy's interpreter:
@ -794,32 +796,6 @@ code: undefined symbol "i"
</details>
<details><summary>Default dynamic arguments not supported</summary>
In this interpreter,
function doesn't put dynamic args into vector `arg` by default.
So if you use `arg` without definition,
you'll get an error of `undefined symbol`.
```javascript
var f=func(){
println(arg)
}
f(1,2,3);
```
Compilation result:
```javascript
code: undefined symbol "arg"
--> test.nas:2:15
|
2 | println(arg)
| ^ undefined symbol "arg"
```
</details>
## __Trace Back Info__
![stackoverflow](./doc/gif/stackoverflow.gif)

View File

@ -735,6 +735,7 @@ dylib.dlclose(dlhandle.lib);
<details><summary>必须用 var 定义变量</summary>
这个解释器使用了更加严格的语法检查来保证你可以更轻松地debug。这是非常有必要的严格否则debug会非常痛苦。
同样的flightgear 内置的 nasal 解释器也采取了类似的措施,所以使用变量前务必用 `var` 先进行声明。
在Andy的解释器中:
@ -762,29 +763,6 @@ code: undefined symbol "i"
```
</details>
<details><summary>默认不定长参数</summary>
这个解释器在运行时,函数不会将超出参数表的那部分不定长参数放到默认的`arg`中。所以你如果不定义`arg`就使用它,那你只会得到`undefined symbol`。
```javascript
var f=func(){
println(arg)
}
f(1,2,3);
```
编译结果:
```javascript
code: undefined symbol "arg"
--> test.nas:2:15
|
2 | println(arg)
| ^ undefined symbol "arg"
```
</details>
## __堆栈追踪信息__
![stackoverflow](../doc/gif/stackoverflow.gif)

View File

@ -1,5 +1,23 @@
STD=c++17
NASAL_HEADER=\
src/ast_dumper.h\
src/ast_visitor.h\
src/nasal_ast.h\
src/nasal_builtin.h\
src/nasal_codegen.h\
src/nasal_dbg.h\
src/nasal_err.h\
src/nasal_gc.h\
src/nasal_import.h\
src/nasal_lexer.h\
src/nasal_opcode.h\
src/nasal_parse.h\
src/nasal_vm.h\
src/nasal.h\
src/optimizer.h\
src/symbol_finder.h
NASAL_OBJECT=\
build/nasal_err.o\
build/nasal_ast.o\
@ -29,55 +47,96 @@ nasal.exe: $(NASAL_OBJECT) | build
build:
@ if [ ! -d build ]; then mkdir build; fi
build/main.o: src/nasal.h src/nasal_err.h src/nasal_lexer.h src/main.cpp | build
build/main.o: $(NASAL_HEADER) src/main.cpp | build
$(CXX) -std=$(STD) -c -O3 src/main.cpp -fno-exceptions -fPIC -o build/main.o -I .
build/nasal_misc.o: src/nasal.h src/nasal_misc.cpp | build
$(CXX) -std=$(STD) -c -O3 src/nasal_misc.cpp -fno-exceptions -fPIC -o build/nasal_misc.o -I .
build/nasal_err.o: src/nasal_err.h src/nasal_err.cpp | build
build/nasal_err.o: src/nasal.h src/nasal_err.h src/nasal_err.cpp | build
$(CXX) -std=$(STD) -c -O3 src/nasal_err.cpp -fno-exceptions -fPIC -o build/nasal_err.o -I .
build/nasal_gc.o: src/nasal_gc.h src/nasal_gc.cpp | build
build/nasal_gc.o: src/nasal.h src/nasal_gc.h src/nasal_gc.cpp | build
$(CXX) -std=$(STD) -c -O3 src/nasal_gc.cpp -fno-exceptions -fPIC -o build/nasal_gc.o -I .
build/nasal_import.o: src/nasal_import.h src/nasal_import.cpp | build
build/nasal_import.o: \
src/nasal.h\
src/nasal_ast.h\
src/nasal_lexer.h\
src/nasal_parse.h\
src/nasal_import.h src/nasal_import.cpp | build
$(CXX) -std=$(STD) -c -O3 src/nasal_import.cpp -fno-exceptions -fPIC -o build/nasal_import.o -I .
build/nasal_lexer.o: src/nasal_lexer.h src/nasal_lexer.cpp | build
build/nasal_lexer.o: \
src/nasal.h\
src/nasal_err.h\
src/nasal_lexer.h src/nasal_lexer.cpp | build
$(CXX) -std=$(STD) -c -O3 src/nasal_lexer.cpp -fno-exceptions -fPIC -o build/nasal_lexer.o -I .
build/nasal_ast.o: src/nasal_ast.h src/nasal_ast.cpp | build
build/nasal_ast.o: \
src/nasal.h\
src/nasal_err.h\
src/nasal_ast.h src/nasal_ast.cpp | build
$(CXX) -std=$(STD) -c -O3 src/nasal_ast.cpp -fno-exceptions -fPIC -o build/nasal_ast.o -I .
build/nasal_builtin.o: src/nasal_builtin.h src/nasal_builtin.cpp | build
build/nasal_builtin.o: \
src/nasal.h\
src/nasal_gc.h\
src/nasal_builtin.h src/nasal_builtin.cpp | build
$(CXX) -std=$(STD) -c -O3 src/nasal_builtin.cpp -fno-exceptions -fPIC -o build/nasal_builtin.o -I .
build/nasal_codegen.o: src/nasal_codegen.h src/nasal_codegen.cpp | build
build/nasal_codegen.o: $(NASAL_HEADER) src/nasal_codegen.h src/nasal_codegen.cpp | build
$(CXX) -std=$(STD) -c -O3 src/nasal_codegen.cpp -fno-exceptions -fPIC -o build/nasal_codegen.o -I .
build/nasal_opcode.o: src/nasal_opcode.h src/nasal_opcode.cpp | build
build/nasal_opcode.o: \
src/nasal.h\
src/nasal_builtin.h\
src/nasal_opcode.h src/nasal_opcode.cpp | build
$(CXX) -std=$(STD) -c -O3 src/nasal_opcode.cpp -fno-exceptions -fPIC -o build/nasal_opcode.o -I .
build/nasal_parse.o: src/nasal_parse.h src/nasal_parse.cpp src/nasal_ast.h | build
build/nasal_parse.o: \
src/nasal.h\
src/nasal_ast.h\
src/nasal_lexer.h\
src/nasal_err.h\
src/nasal_parse.h src/nasal_parse.cpp src/nasal_ast.h | build
$(CXX) -std=$(STD) -c -O3 src/nasal_parse.cpp -fno-exceptions -fPIC -o build/nasal_parse.o -I .
build/optimizer.o: src/optimizer.h src/optimizer.cpp src/nasal_ast.h | build
build/optimizer.o: \
src/nasal.h\
src/nasal_err.h\
src/nasal_ast.h\
src/ast_visitor.h\
src/optimizer.h src/optimizer.cpp src/nasal_ast.h | build
$(CXX) -std=$(STD) -c -O3 src/optimizer.cpp -fno-exceptions -fPIC -o build/optimizer.o -I .
build/symbol_finder.o: src/symbol_finder.h src/symbol_finder.cpp src/nasal_ast.h | build
build/symbol_finder.o: \
src/nasal.h\
src/nasal_err.h\
src/nasal_ast.h\
src/ast_visitor.h\
src/symbol_finder.h src/symbol_finder.cpp src/nasal_ast.h | build
$(CXX) -std=$(STD) -c -O3 src/symbol_finder.cpp -fno-exceptions -fPIC -o build/symbol_finder.o -I .
build/ast_visitor.o: src/nasal_ast.h src/ast_visitor.h src/ast_visitor.cpp | build
build/ast_visitor.o: \
src/nasal.h\
src/nasal_err.h\
src/nasal_ast.h\
src/ast_visitor.h src/ast_visitor.cpp | build
$(CXX) -std=$(STD) -c -O3 src/ast_visitor.cpp -fno-exceptions -fPIC -o build/ast_visitor.o -I .
build/ast_dumper.o: src/nasal_ast.h src/ast_visitor.h src/ast_dumper.h src/ast_dumper.cpp
build/ast_dumper.o: \
src/nasal.h\
src/nasal_err.h\
src/nasal_ast.h\
src/ast_visitor.h\
src/ast_dumper.h src/ast_dumper.cpp | build
$(CXX) -std=$(STD) -c -O3 src/ast_dumper.cpp -fno-exceptions -fPIC -o build/ast_dumper.o -I .
build/nasal_vm.o: src/nasal_vm.h src/nasal_vm.cpp | build
build/nasal_vm.o: $(NASAL_HEADER) src/nasal_vm.h src/nasal_vm.cpp | build
$(CXX) -std=$(STD) -c -O3 src/nasal_vm.cpp -fno-exceptions -fPIC -o build/nasal_vm.o -I .
build/nasal_dbg.o: src/nasal_dbg.h src/nasal_dbg.cpp | build
build/nasal_dbg.o: $(NASAL_HEADER) src/nasal_dbg.h src/nasal_dbg.cpp | build
$(CXX) -std=$(STD) -c -O3 src/nasal_dbg.cpp -fno-exceptions -fPIC -o build/nasal_dbg.o -I .
.PHONY: clean

View File

@ -16,7 +16,7 @@ var fib(var* args, usize size, gc* ngc) {
if (!size) {
return nas_err("fib","lack arguments");
}
var num=args[0];
var num = args[0];
return var::num(fibonaci(num.tonum()));
}
@ -24,15 +24,15 @@ var quick_fib(var* args, usize size, gc* ngc) {
if (!size) {
return nas_err("quick_fib","lack arguments");
}
double num=args[0].tonum();
double num = args[0].tonum();
if (num<2) {
return var::num(num);
}
double a=1,b=1,res=0;
for(double i=1;i<num;i+=1) {
res=a+b;
a=b;
b=res;
double a = 1, b = 1, res = 0;
for(double i = 1; i<num; ++i) {
res = a+b;
a = b;
b = res;
}
return var::num(res);
}
@ -40,42 +40,43 @@ var quick_fib(var* args, usize size, gc* ngc) {
u32 ghost_for_test;
void ghost_for_test_destructor(void* ptr) {
std::cout<<"ghost_for_test::destructor (0x";
std::cout<<std::hex<<(u64)ptr<<std::dec<<") {\n";
std::cout << "ghost_for_test::destructor (0x";
std::cout << std::hex << (u64)ptr << std::dec << ") {\n";
delete (u32*)ptr;
std::cout<<" delete 0x"<<std::hex<<(u64)ptr<<std::dec<<";\n";
std::cout<<"}\n";
std::cout << " delete 0x" << std::hex << (u64)ptr << std::dec << ";\n";
std::cout << "}\n";
}
var create_new_ghost(var* args, usize size, gc* ngc) {
var res=ngc->alloc(vm_obj);
var res = ngc->alloc(vm_obj);
res.obj().set(ghost_for_test, new u32, &ngc->global_ghost_type_table);
return res;
}
var set_new_ghost(var* args, usize size, gc* ngc) {
var res=args[0];
var res = args[0];
if (!res.objchk(ghost_for_test)) {
std::cout<<"set_new_ghost: not ghost for test type.\n";
std::cout << "set_new_ghost: not ghost for test type.\n";
return nil;
}
f64 num=args[1].num();
*((u32*)res.obj().ptr)=static_cast<u32>(num);
std::cout<<"set_new_ghost: successfully set ghost = "<<num<<"\n";
f64 num = args[1].num();
*((u32*)res.obj().ptr) = static_cast<u32>(num);
std::cout << "set_new_ghost: successfully set ghost = " << num << "\n";
return nil;
}
var print_new_ghost(var* args, usize size, gc* ngc) {
var res=args[0];
var res = args[0];
if (!res.objchk(ghost_for_test)) {
std::cout<<"print_new_ghost: not ghost for test type.\n";
std::cout << "print_new_ghost: not ghost for test type.\n";
return nil;
}
std::cout<<"print_new_ghost: "<<res.obj()<<" result = "<<*((u32*)res.obj().ptr)<<"\n";
std::cout << "print_new_ghost: " << res.obj() << " result = "
<< *((u32*)res.obj().ptr) << "\n";
return nil;
}
module_func_info func_tbl[]={
module_func_info func_tbl[] = {
{"fib", fib},
{"quick_fib", quick_fib},
{"create_ghost", create_new_ghost},
@ -88,10 +89,10 @@ module_func_info func_tbl[]={
extern "C" module_func_info* get(ghost_register_table* table) {
if (table->exists("fib_for_test")) {
nasal_fib_module::ghost_for_test=table->get_ghost_type_index("fib_for_test");
nasal_fib_module::ghost_for_test = table->get_ghost_type_index("fib_for_test");
return nasal_fib_module::func_tbl;
}
nasal_fib_module::ghost_for_test=table->register_ghost_type(
nasal_fib_module::ghost_for_test = table->register_ghost_type(
"fib_for_test",
nasal_fib_module::ghost_for_test_destructor
);

View File

@ -145,7 +145,7 @@ void execute(
// get running time
if (cmd&VM_TIME) {
f64 tm = (clk::now()-start).count()*1.0/den;
std::clog << "process exited after " << tm << "s.\n\n";
std::clog << " process exited after " << tm << "s.\n\n";
}
}

View File

@ -32,7 +32,7 @@ using u64 = std::uint64_t;
using usize = std::size_t;
using f64 = double;
const u32 STACK_DEPTH=4096;
const u32 STACK_DEPTH = 4096;
f64 hex2f(const char*);
f64 oct2f(const char*);

View File

@ -1210,12 +1210,6 @@ var builtin_millisec(var* local, gc& ngc) {
return var::num(res);
}
var builtin_sysargv(var* local, gc& ngc) {
var res = ngc.alloc(vm_vec);
res.vec().elems = ngc.env_argv;
return res;
}
var builtin_gcextend(var* local, gc& ngc) {
var type = local[1];
if (type.type!=vm_str) {
@ -1356,7 +1350,6 @@ nasal_builtin_table builtin[] = {
{"__costatus", builtin_costatus},
{"__corun", builtin_corun},
{"__millisec", builtin_millisec},
{"__sysargv", builtin_sysargv},
{"__gcextd", builtin_gcextend},
{"__logtime", builtin_logtime},
{"__ghosttype", builtin_ghosttype},

View File

@ -124,7 +124,6 @@ var builtin_coyield(var*, gc&);
var builtin_costatus(var*, gc&);
var builtin_corun(var*, gc&);
var builtin_millisec(var*, gc&);
var builtin_sysargv(var*, gc&);
var builtin_gcextend(var*, gc&);
var builtin_logtime(var*, gc&);
var builtin_ghosttype(var*, gc&);

View File

@ -211,6 +211,21 @@ void codegen::func_gen(function* node) {
// search symbols first, must use after loading parameters
// or the location of symbols will change and cause fatal error
find_symbol(block);
// add special varibale "arg", which is used to store overflowed args
// but if dynamic parameter is declared, this variable will be useless
// for example:
// var f = func(a) {print(arg)}
// f(1, 2, 3);
// then the arg is [2, 3], because 1 is accepted by "a"
// so in fact "f" is the same as:
// var f = func(a, arg...) {return(arg)}
auto arg = std::string("arg");
// this is used to avoid confliction with defined parameter
while(local_find(arg)>=0) {
arg = "0" + arg;
}
add_symbol(arg);
in_iterloop.push(0);
block_gen(block);
in_iterloop.pop();
@ -1094,13 +1109,7 @@ const error& codegen::compile(parse& parse, linker& import) {
// add special symbol globals, which is a hash stores all global variables
add_symbol("globals");
// add special symbol arg here, which is used to store function arguments
// for example:
// var f = func(a) {print(arg)}
// f(1, 2, 3);
// then the arg is [2, 3], because 1 is accepted by "a"
// so in fact "f" is the same as:
// var f = func(a, arg...) {return(arg)}
// add special symbol arg here, which is used to store command line args
add_symbol("arg");
find_symbol(parse.tree()); // search symbols first

View File

@ -2,14 +2,13 @@
std::vector<std::string> dbg::parse(const std::string& cmd) {
std::vector<std::string> res;
usize last=0;
usize pos=cmd.find(" ", 0);
usize last = 0, pos = cmd.find(" ", 0);
while(pos!=std::string::npos) {
if (pos>last) {
res.push_back(cmd.substr(last, pos-last));
}
last=pos+1;
pos=cmd.find(" ", last);
last = pos+1;
pos = cmd.find(" ", last);
}
if (last<cmd.length()) {
res.push_back(cmd.substr(last));
@ -18,7 +17,7 @@ std::vector<std::string> dbg::parse(const std::string& cmd) {
}
u16 dbg::file_index(const std::string& filename) const {
for(u16 i=0;i<fsize;++i) {
for(u16 i = 0; i<fsize; ++i) {
if (filename==files[i]) {
return i;
}
@ -28,76 +27,78 @@ u16 dbg::file_index(const std::string& filename) const {
void dbg::err() {
std::cerr
<<"incorrect command\n"
<<"input \'h\' to get help\n";
<< "incorrect command\n"
<< "input \'h\' to get help\n";
}
void dbg::help() {
std::clog
<<"<option>\n"
<<" h, help | get help\n"
<<" bt, backtrace | get function call trace\n"
<<" c, continue | run program until break point or exit\n"
<<" f, file | see all the compiled files\n"
<<" g, global | see global values\n"
<<" l, local | see local values\n"
<<" u, upval | see upvalue\n"
<<" r, register | show vm register detail\n"
<<" a, all | show global,local and upvalue\n"
<<" n, next | execute next bytecode\n"
<<" q, exit | exit debugger\n"
<<"<option> <filename> <line>\n"
<<" bk, break | set break point\n";
<< "<option>\n"
<< " h, help | get help\n"
<< " bt, backtrace | get function call trace\n"
<< " c, continue | run program until break point or exit\n"
<< " f, file | see all the compiled files\n"
<< " g, global | see global values\n"
<< " l, local | see local values\n"
<< " u, upval | see upvalue\n"
<< " r, register | show vm register detail\n"
<< " a, all | show global,local and upvalue\n"
<< " n, next | execute next bytecode\n"
<< " q, exit | exit debugger\n"
<< "<option> <filename> <line>\n"
<< " bk, break | set break point\n";
}
void dbg::list_file() const {
for(usize i=0; i<fsize; ++i) {
std::clog<<"["<<i<<"] "<<files[i]<<"\n";
for(usize i = 0; i<fsize; ++i) {
std::clog << "[" << i << "] " << files[i] << "\n";
}
}
void dbg::call_sort(const u64* arr) const {
typedef std::pair<u32,u64> op;
std::vector<op> opcall;
u64 total=0;
for(u32 i=0;i<op_ret+1;++i) {
total+=arr[i];
u64 total = 0;
for(u32 i = 0; i<op_ret+1; ++i) {
total += arr[i];
opcall.push_back({i, arr[i]});
}
std::sort(opcall.begin(), opcall.end(),
[](const op& a, const op& b) {return a.second>b.second;}
);
std::clog<<"\noperands call info (<1% ignored)\n";
for(auto& i:opcall) {
u64 rate=i.second*100/total;
std::clog << "\noperands call info (<1% ignored)\n";
for(auto& i : opcall) {
u64 rate = i.second*100/total;
if (!rate) {
break;
}
std::clog<<" "<<opname[i.first]<<" : "<<i.second<<" ("<<rate<<"%)\n";
std::clog << " " << opname[i.first] << " : ";
std::clog << i.second << " (" << rate << "%)\n";
}
std::clog<<" total : "<<total<<'\n';
std::clog << " total : " << total << '\n';
}
void dbg::step_info() {
u32 line=bytecode[ctx.pc].line==0?0:bytecode[ctx.pc].line-1;
u32 begin=(line>>3)==0?0:((line>>3)<<3);
u32 end=(1+(line>>3))<<3;
u32 line = bytecode[ctx.pc].line==0? 0:bytecode[ctx.pc].line-1;
u32 begin = (line>>3)==0? 0:((line>>3)<<3);
u32 end = (1+(line>>3))<<3;
src.load(files[bytecode[ctx.pc].fidx]);
std::clog<<"\nsource code:\n";
for(u32 i=begin;i<end && i<src.size();++i) {
std::clog<<(i==line?back_white:reset)<<(i==line?"--> ":" ")<<src[i]<<reset<<"\n";
std::clog << "\nsource code:\n";
for(u32 i = begin; i<end && i<src.size(); ++i) {
std::clog << (i==line? back_white:reset);
std::clog << (i==line? "--> ":" ") << src[i] << reset << "\n";
}
begin=(ctx.pc>>3)==0?0:((ctx.pc>>3)<<3);
end=(1+(ctx.pc>>3))<<3;
begin = (ctx.pc>>3)==0? 0:((ctx.pc>>3)<<3);
end = (1+(ctx.pc>>3))<<3;
codestream::set(cnum, cstr, files);
std::clog<<"next bytecode:\n";
for(u32 i=begin;i<end && bytecode[i].op!=op_exit;++i) {
std::clog << "next bytecode:\n";
for(u32 i = begin; i<end && bytecode[i].op!=op_exit; ++i) {
std::clog
<<(i==ctx.pc?back_white:reset)
<<(i==ctx.pc?"--> ":" ")
<<codestream(bytecode[i], i)
<<reset<<"\n";
<< (i==ctx.pc? back_white:reset)
<< (i==ctx.pc? "--> ":" ")
<< codestream(bytecode[i], i)
<< reset << "\n";
}
stackinfo(10);
}
@ -114,11 +115,11 @@ void dbg::interact() {
return;
}
next=false;
next = false;
std::string cmd;
step_info();
while(true) {
std::clog<<">> ";
std::clog << ">> ";
std::getline(std::cin, cmd);
auto res=parse(cmd);
if (res.size()==0) {
@ -134,22 +135,22 @@ void dbg::interact() {
case dbg_cmd::cmd_upval: ustate(); break;
case dbg_cmd::cmd_register: reginfo(); break;
case dbg_cmd::cmd_show_all: detail(); break;
case dbg_cmd::cmd_next: next=true; return;
case dbg_cmd::cmd_next: next = true; return;
case dbg_cmd::cmd_exit: std::exit(0);
default: err(); break;
}
} else if (res.size()==3 &&
get_cmd_type(res[0])==dbg_cmd::cmd_break_point) {
bk_fidx=file_index(res[1]);
bk_fidx = file_index(res[1]);
if (bk_fidx==65535) {
std::clog<<"cannot find file named `"<<res[1]<<"`\n";
std::clog << "cannot find file named `" << res[1] << "`\n";
continue;
}
i32 tmp=atoi(res[2].c_str());
i32 tmp = atoi(res[2].c_str());
if (tmp<=0) {
std::clog<<"incorrect line number `"<<res[2]<<"`\n";
std::clog << "incorrect line number `" << res[2] << "`\n";
} else {
bk_line=tmp;
bk_line = tmp;
}
} else {
err();
@ -161,17 +162,17 @@ void dbg::run(
const codegen& gen,
const linker& linker,
const std::vector<std::string>& argv) {
verbose=true;
fsize=linker.filelist().size();
verbose = true;
fsize = linker.filelist().size();
init(gen.strs(),
gen.nums(),
gen.codes(),
gen.globals(),
linker.filelist(),
argv);
u64 count[op_ret+1]={0};
u64 count[op_ret+1] = {0};
typedef void (dbg::*nafunc)();
const nafunc oprs[]={
const nafunc oprs[] = {
nullptr, &dbg::o_intg,
&dbg::o_intl, &dbg::o_loadg,
&dbg::o_loadl, &dbg::o_loadu,
@ -218,7 +219,7 @@ void dbg::run(
&dbg::o_ret
};
std::vector<u32> code;
for(auto& i:gen.codes()) {
for(auto& i : gen.codes()) {
code.push_back(i.op);
imm.push_back(i.num);
}

View File

@ -13,7 +13,7 @@ std::ostream& back_white(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0xf0);
#else
s<<"\033[7m";
s << "\033[7m";
#endif
return s;
}
@ -22,7 +22,7 @@ std::ostream& red(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0c);
#else
s<<"\033[91;1m";
s << "\033[91;1m";
#endif
return s;
}
@ -31,7 +31,7 @@ std::ostream& cyan(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x03);
#else
s<<"\033[36;1m";
s << "\033[36;1m";
#endif
return s;
}
@ -40,7 +40,7 @@ std::ostream& orange(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0e);
#else
s<<"\033[93;1m";
s << "\033[93;1m";
#endif
return s;
}
@ -49,7 +49,7 @@ std::ostream& white(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0f);
#else
s<<"\033[0m\033[1m";
s << "\033[0m\033[1m";
#endif
return s;
}
@ -58,7 +58,7 @@ std::ostream& reset(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), reset_ter_color.scr.wAttributes);
#else
s<<"\033[0m";
s << "\033[0m";
#endif
return s;
}
@ -73,7 +73,7 @@ void flstream::load(const std::string& f) {
res.clear();
std::ifstream in(f, std::ios::binary);
if (in.fail()) {
std::cerr<<red<<"src: "<<reset<<"cannot open <"<<f<<">\n";
std::cerr << red << "src: " << reset << "cannot open <" << f << ">\n";
std::exit(1);
}
@ -85,22 +85,22 @@ void flstream::load(const std::string& f) {
}
void error::fatal(const std::string& stage, const std::string& info) {
std::cerr<<red<<stage<<": "<<white<<info<<reset<<"\n";
std::cerr << red << stage << ": " << white << info << reset << "\n";
if (file.length()) {
std::cerr<<cyan<<" --> "<<red<<file<<reset<<"\n\n";
std::cerr << cyan << " --> " << red << file << reset << "\n\n";
} else {
std::cerr<<reset<<"\n";
std::cerr << reset << "\n";
}
std::exit(1);
}
void error::err(const std::string& stage, const std::string& info) {
++cnt;
std::cerr<<red<<stage<<": "<<white<<info<<reset<<"\n";
std::cerr << red << stage << ": " << white << info << reset << "\n";
if (file.length()) {
std::cerr<<cyan<<" --> "<<red<<file<<reset<<"\n\n";
std::cerr << cyan << " --> " << red << file << reset << "\n\n";
} else {
std::cerr<<reset<<"\n";
std::cerr << reset << "\n";
}
}
@ -112,8 +112,9 @@ void error::err(
++cnt;
std::cerr
<<red<<stage<<": "<<white<<info<<reset<<"\n"<<cyan<<" --> "
<<red<<loc.file<<":"<<loc.begin_line<<":"<<loc.begin_column+1<<reset<<"\n";
<< red << stage << ": " << white << info << reset << "\n" << cyan << " --> "
<< red << loc.file << ":" << loc.begin_line << ":" << loc.begin_column+1
<< reset << "\n";
const usize maxlen = std::to_string(loc.end_line).length();
const std::string iden = identation(maxlen);
@ -125,7 +126,8 @@ void error::err(
if (loc.begin_line<line && line<loc.end_line) {
if (line==loc.begin_line+1) {
std::cerr<<cyan<<iden<<" | "<<reset<<"...\n"<<cyan<<iden<<" | "<<reset<<"\n";
std::cerr << cyan << iden << " | " << reset << "...\n"
<< cyan << iden << " | " << reset << "\n";
}
continue;
}
@ -136,37 +138,37 @@ void error::err(
}
const std::string& code=res[line-1];
std::cerr<<cyan<<leftpad(line, maxlen)<<" | "<<reset<<code<<"\n";
std::cerr << cyan << leftpad(line, maxlen) << " | " << reset << code << "\n";
// output underline
std::cerr<<cyan<<iden<<" | "<<reset;
std::cerr << cyan << iden << " | " << reset;
if (loc.begin_line==loc.end_line) {
for(u32 i=0; i<loc.begin_column; ++i) {
std::cerr<<char(" \t"[code[i]=='\t']);
for(u32 i = 0; i<loc.begin_column; ++i) {
std::cerr << char(" \t"[code[i]=='\t']);
}
for(u32 i=loc.begin_column ;i<loc.end_column; ++i) {
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^")<<reset;
for(u32 i = loc.begin_column ;i<loc.end_column; ++i) {
std::cerr << red << (code[i]=='\t'? "^^^^":"^") << reset;
}
} else if (line==loc.begin_line) {
for(u32 i=0; i<loc.begin_column; ++i) {
std::cerr<<char(" \t"[code[i]=='\t']);
for(u32 i = 0; i<loc.begin_column; ++i) {
std::cerr << char(" \t"[code[i]=='\t']);
}
for(u32 i=loc.begin_column; i<code.size(); ++i) {
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^")<<reset;
for(u32 i = loc.begin_column; i<code.size(); ++i) {
std::cerr << red << (code[i]=='\t'? "^^^^":"^") << reset;
}
} else if (loc.begin_line<line && line<loc.end_line) {
for(u32 i=0; i<code.size(); ++i) {
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^");
for(u32 i = 0; i<code.size(); ++i) {
std::cerr << red << (code[i]=='\t'? "^^^^":"^");
}
} else {
for(u32 i=0; i<loc.end_column; ++i) {
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^");
for(u32 i = 0; i<loc.end_column; ++i) {
std::cerr << red << (code[i]=='\t'? "^^^^":"^");
}
}
if (line==loc.end_line) {
std::cerr<<reset;
std::cerr << reset;
} else {
std::cerr<<reset<<"\n";
std::cerr << reset << "\n";
}
}
std::cerr<<"\n\n";
std::cerr << "\n\n";
}

View File

@ -337,6 +337,23 @@ nas_map& var::map() {
return *val.gcobj->ptr.map;
}
void gc::do_mark_sweep() {
using clk = std::chrono::high_resolution_clock;
auto begin = clk::now();
mark();
auto mark_end = clk::now();
sweep();
auto sweep_end = clk::now();
auto total_time = (sweep_end-begin).count();
auto mark_time = (mark_end-begin).count();
auto sweep_time = (sweep_end-mark_end).count();
worktime += total_time;
max_time = max_time<total_time? total_time:max_time;
max_mark_time = max_mark_time<mark_time? mark_time:max_mark_time;
max_sweep_time = max_sweep_time<sweep_time? sweep_time:max_sweep_time;
}
void gc::mark() {
std::vector<var> bfs;
mark_context(bfs);
@ -389,13 +406,17 @@ 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) {
bfs_queue.push_back(i);
if (i.type>vm_num) {
bfs_queue.push_back(i);
}
}
}
void gc::mark_hash(std::vector<var>& bfs_queue, nas_hash& hash) {
for(auto& i : hash.elems) {
bfs_queue.push_back(i.second);
if (i.second.type>vm_num) {
bfs_queue.push_back(i.second);
}
}
}
@ -502,24 +523,39 @@ void gc::clear() {
env_argv.clear();
}
void gc::info() {
void gc::info() const {
using std::left;
using std::setw;
using std::setfill;
const char* used_table_name[] = {
"object type", "gc count", "alloc count", "memory size",
"detail", "time spend", "gc time", "avg time", "max gc",
"max mark", "max sweep", nullptr
};
const char* name[] = {
"string ",
"vector ",
"hashmap ",
"function ",
"upvalue ",
"object ",
"string",
"vector",
"hashmap",
"function",
"upvalue",
"object",
"coroutine",
"mapper "
"mapper",
nullptr
};
usize indent = 0;
for(u8 i = 0; i<gc_type_size; ++i) {
usize len = 0;
usize indent = 0, len = 0;
for(usize i = 0; used_table_name[i]; ++i) {
len = std::string(used_table_name[i]).length();
indent = indent<len? len:indent;
}
for(usize i = 0; name[i]; ++i) {
len = std::string(name[i]).length();
indent = indent<len? len:indent;
}
for(u32 i = 0; i<gc_type_size; ++i) {
len = std::to_string(gcnt[i]).length();
indent = indent<len? len:indent;
len = std::to_string(acnt[i]).length();
@ -527,51 +563,60 @@ void gc::info() {
len = std::to_string(size[i]).length();
indent = indent<len? len:indent;
}
auto indent_string = std::string("--");
for(usize i = 0; i<indent; ++i) {
indent_string += "-";
}
indent_string = indent_string + "+" +
indent_string + "+" + indent_string + "+" + indent_string;
std::clog << "\n" << indent_string << "\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "object type";
std::clog << " | " << left << setw(indent) << setfill(' ') << "gc count";
std::clog << " | " << left << setw(indent) << setfill(' ') << "alloc count";
std::clog << " | " << left << setw(indent) << setfill(' ') << "memory size";
std::clog << "\n" << indent_string << "\n";
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]) {
continue;
}
total += gcnt[i];
std::clog << " " << name[i];
std::clog << " " << left << setw(indent) << setfill(' ') << name[i];
std::clog << " | " << left << setw(indent) << setfill(' ') << gcnt[i];
std::clog << " | " << left << setw(indent) << setfill(' ') << acnt[i];
std::clog << " | " << left << setw(indent) << setfill(' ') << size[i];
std::clog << "\n";
}
std::clog << indent_string << "\n";
auto den = std::chrono::high_resolution_clock::duration::period::den;
std::clog << " gc time | " << worktime*1.0/den*1000 << " ms\n";
if (total) {
std::clog << " avg time | " << worktime*1.0/den*1000/total << " ms\n";
std::clog << " max gc | " << max_time*1.0/den*1000 << " ms\n";
std::clog << " max mark | " << max_mark_time*1.0/den*1000 << " ms\n";
std::clog << " max sweep | " << max_sweep_time*1.0/den*1000 << " ms\n";
}
std::clog<<"\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "detail";
std::clog << " | " << left << setw(indent) << setfill(' ') << "time spend";
std::clog << " | " << left << setw(indent) << setfill('x') << "x";
std::clog << " | " << left << setw(indent) << setfill('x') << "x";
std::clog << "\n" << indent_string << "\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "gc time";
std::clog << " | " << worktime*1.0/den*1000 << " ms\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "avg time";
std::clog << " | " << worktime*1.0/den*1000/total << " ms\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "max gc";
std::clog << " | " << max_time*1.0/den*1000 << " ms\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "max mark";
std::clog << " | " << max_mark_time*1.0/den*1000 << " ms\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "max sweep";
std::clog << " | " << max_sweep_time*1.0/den*1000 << " ms\n";
std::clog << indent_string << "\n";
}
var gc::alloc(u8 type) {
using clk = std::chrono::high_resolution_clock;
const u8 index = type-vm_str;
++acnt[index];
if (unused[index].empty()) {
++gcnt[index];
auto begin = clk::now();
mark();
auto mark_end = clk::now();
sweep();
auto sweep_end = clk::now();
auto total_time = (sweep_end-begin).count();
auto mark_time = (mark_end-begin).count();
auto sweep_time = (sweep_end-mark_end).count();
worktime += total_time;
max_time = max_time<total_time? total_time:max_time;
max_mark_time = max_mark_time<mark_time? mark_time:max_mark_time;
max_sweep_time = max_sweep_time<sweep_time? sweep_time:max_sweep_time;
do_mark_sweep();
}
if (unused[index].empty()) {
extend(type);

View File

@ -383,6 +383,7 @@ struct gc {
private:
/* gc functions */
void do_mark_sweep();
void mark();
void mark_context(std::vector<var>&);
void mark_var(std::vector<var>&, var&);
@ -398,7 +399,7 @@ public:
void extend(u8);
void init(const std::vector<std::string>&, const std::vector<std::string>&);
void clear();
void info();
void info() const;
var alloc(const u8);
void ctxchg(nas_co&);
void ctxreserve();

View File

@ -1,17 +1,17 @@
#include "nasal_import.h"
linker::linker(error& e): show_path(false), lib_loaded(false), err(e) {
char sep=is_windows()? ';':':';
std::string PATH=getenv("PATH");
usize last=0;
usize pos=PATH.find(sep, 0);
linker::linker(error& e):
show_path(false), lib_loaded(false), err(e) {
char sep = is_windows()? ';':':';
std::string PATH = getenv("PATH");
usize last = 0, pos = PATH.find(sep, 0);
while(pos!=std::string::npos) {
std::string dirpath=PATH.substr(last, pos-last);
std::string dirpath = PATH.substr(last, pos-last);
if (dirpath.length()) {
envpath.push_back(dirpath);
}
last=pos+1;
pos=PATH.find(sep, last);
last = pos+1;
pos = PATH.find(sep, last);
}
if (last!=PATH.length()) {
envpath.push_back(PATH.substr(last));
@ -132,7 +132,7 @@ bool linker::exist(const std::string& file) {
void linker::link(code_block* new_tree_root, code_block* old_tree_root) {
// add children of add_root to the back of root
for(auto& i:old_tree_root->get_expressions()) {
for(auto& i : old_tree_root->get_expressions()) {
new_tree_root->add_expression(i);
}
// clean old root

View File

@ -7,43 +7,43 @@
#include "nasal_lexer.h"
bool lexer::skip(char c) {
return c==' '||c=='\n'||c=='\t'||c=='\r'||c==0;
return c==' ' || c=='\n' || c=='\t' || c=='\r' || c==0;
}
bool lexer::is_id(char c) {
return (c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z')||(c<0);
return (c=='_') || ('a'<=c && c<='z') || ('A'<=c && c<='Z') || (c<0);
}
bool lexer::is_hex(char c) {
return ('0'<=c&&c<='9')||('a'<=c&&c<='f')||('A'<=c && c<='F');
return ('0'<=c && c<='9') || ('a'<=c && c<='f') || ('A'<=c && c<='F');
}
bool lexer::is_oct(char c) {
return '0'<=c&&c<='7';
return '0'<=c && c<='7';
}
bool lexer::is_dec(char c) {
return '0'<=c&&c<='9';
return '0'<=c && c<='9';
}
bool lexer::is_str(char c) {
return c=='\''||c=='\"'||c=='`';
return c=='\'' || c=='\"' || c=='`';
}
bool lexer::is_single_opr(char c) {
return (
c=='('||c==')'||c=='['||c==']'||
c=='{'||c=='}'||c==','||c==';'||
c==':'||c=='?'||c=='`'||c=='@'||
c=='%'||c=='$'||c=='\\'
c=='(' || c==')' || c=='[' || c==']' ||
c=='{' || c=='}' || c==',' || c==';' ||
c==':' || c=='?' || c=='`' || c=='@' ||
c=='%' || c=='$' || c=='\\'
);
}
bool lexer::is_calc_opr(char c) {
return (
c=='='||c=='+'||c=='-'||c=='*'||
c=='!'||c=='/'||c=='<'||c=='>'||
c=='~'||c=='|'||c=='&'||c=='^'
c=='=' || c=='+' || c=='-' || c=='*' ||
c=='!' || c=='/' || c=='<' || c=='>' ||
c=='~' || c=='|' || c=='&' || c=='^'
);
}
@ -54,8 +54,10 @@ void lexer::skip_note() {
void lexer::err_char() {
++column;
char c=res[ptr++];
err.err("lexer", {line, column-1, line, column, filename}, "invalid character 0x"+chrhex(c));
char c = res[ptr++];
err.err("lexer",
{line, column-1, line, column, filename},
"invalid character 0x"+chrhex(c));
err.fatal("lexer", "fatal error occurred, stop");
}
@ -81,220 +83,241 @@ void lexer::open(const std::string& file) {
}
tok lexer::get_type(const std::string& str) {
return typetbl.count(str)?typetbl.at(str):tok::null;
return typetbl.count(str)? typetbl.at(str):tok::null;
}
std::string lexer::utf8_gen() {
std::string str="";
std::string str = "";
while(ptr<res.size() && res[ptr]<0) {
std::string tmp="";
u32 nbytes=utf8_hdchk(res[ptr]);
std::string tmp = "";
u32 nbytes = utf8_hdchk(res[ptr]);
if (!nbytes) {
++ptr;
++column;
continue;
}
tmp+=res[ptr++];
for(u32 i=0;i<nbytes;++i,++ptr) {
tmp += res[ptr++];
for(u32 i = 0; i<nbytes; ++i, ++ptr) {
if (ptr<res.size() && (res[ptr]&0xc0)==0x80) {
tmp+=res[ptr];
tmp += res[ptr];
}
}
// utf8 character's total length is 1+nbytes
if (tmp.length()!=1+nbytes) {
++column;
std::string utf_info="0x"+chrhex(tmp[0]);
for(u32 i=1;i<tmp.size();++i) {
utf_info+=" 0x"+chrhex(tmp[i]);
std::string utf_info = "0x"+chrhex(tmp[0]);
for(u32 i = 1; i<tmp.size(); ++i) {
utf_info += " 0x"+chrhex(tmp[i]);
}
err.err("lexer", {line, column-1, line, column, filename}, "invalid utf-8 <"+utf_info+">");
err.err("lexer",
{line, column-1, line, column, filename},
"invalid utf-8 <"+utf_info+">");
err.fatal("lexer", "fatal error occurred, stop");
}
str+=tmp;
column+=2; // may have some problems because not all the unicode takes 2 space
str += tmp;
column += 2; // may have some problems because not all the unicode takes 2 space
}
return str;
}
token lexer::id_gen() {
u32 begin_line=line;
u32 begin_column=column;
std::string str="";
u32 begin_line = line;
u32 begin_column = column;
std::string str = "";
while(ptr<res.size() && (is_id(res[ptr])||is_dec(res[ptr]))) {
if (res[ptr]<0) { // utf-8
str+=utf8_gen();
str += utf8_gen();
} else { // ascii
str+=res[ptr++];
str += res[ptr++];
++column;
}
}
tok type=get_type(str);
return {{begin_line, begin_column, line, column, filename}, (type!=tok::null)?type:tok::id, str};
tok type = get_type(str);
return {
{begin_line, begin_column, line, column, filename},
(type!=tok::null)? type:tok::id, str};
}
token lexer::num_gen() {
u32 begin_line=line;
u32 begin_column=column;
u32 begin_line = line;
u32 begin_column = column;
// generate hex number
if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='x') {
std::string str="0x";
ptr+=2;
std::string str = "0x";
ptr += 2;
while(ptr<res.size() && is_hex(res[ptr])) {
str+=res[ptr++];
str += res[ptr++];
}
column+=str.length();
if (str.length()<3) { // "0x"
err.err("lexer", {begin_line, begin_column, line, column, filename}, "invalid number `"+str+"`");
column += str.length();
// "0x"
if (str.length()<3) {
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid number `"+str+"`");
}
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
} else if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='o') { // generate oct number
std::string str="0o";
ptr+=2;
std::string str = "0o";
ptr += 2;
while(ptr<res.size() && is_oct(res[ptr])) {
str+=res[ptr++];
str += res[ptr++];
}
bool erfmt=false;
bool erfmt = false;
while(ptr<res.size() && (is_dec(res[ptr]) || is_hex(res[ptr]))) {
erfmt=true;
str+=res[ptr++];
erfmt = true;
str += res[ptr++];
}
column+=str.length();
column += str.length();
if (str.length()==2 || erfmt) {
err.err("lexer", {begin_line, begin_column, line, column, filename}, "invalid number `"+str+"`");
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid number `"+str+"`");
}
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
}
// generate dec number
// dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*)
std::string str="";
std::string str = "";
while(ptr<res.size() && is_dec(res[ptr])) {
str+=res[ptr++];
str += res[ptr++];
}
if (ptr<res.size() && res[ptr]=='.') {
str+=res[ptr++];
str += res[ptr++];
while(ptr<res.size() && is_dec(res[ptr])) {
str+=res[ptr++];
str += res[ptr++];
}
// "xxxx." is not a correct number
if (str.back()=='.') {
column+=str.length();
err.err("lexer",{begin_line, begin_column, line, column, filename}, "invalid number `"+str+"`");
column += str.length();
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid number `"+str+"`");
return {{begin_line, begin_column, line, column, filename}, tok::num, "0"};
}
}
if (ptr<res.size() && (res[ptr]=='e' || res[ptr]=='E')) {
str+=res[ptr++];
str += res[ptr++];
if (ptr<res.size() && (res[ptr]=='-' || res[ptr]=='+')) {
str+=res[ptr++];
str += res[ptr++];
}
while(ptr<res.size() && is_dec(res[ptr])) {
str+=res[ptr++];
str += res[ptr++];
}
// "xxxe(-|+)" is not a correct number
if (str.back()=='e' || str.back()=='E' || str.back()=='-' || str.back()=='+') {
column+=str.length();
err.err("lexer",{begin_line, begin_column, line, column, filename}, "invalid number `"+str+"`");
column += str.length();
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid number `"+str+"`");
return {{begin_line, begin_column, line, column, filename}, tok::num, "0"};
}
}
column+=str.length();
column += str.length();
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
}
token lexer::str_gen() {
u32 begin_line=line;
u32 begin_column=column;
std::string str="";
const char begin=res[ptr];
u32 begin_line = line;
u32 begin_column = column;
std::string str = "";
const char begin = res[ptr];
++column;
while(++ptr<res.size() && res[ptr]!=begin) {
++column;
if (res[ptr]=='\n') {
column=0;
column = 0;
++line;
}
if (res[ptr]=='\\' && ptr+1<res.size()) {
++column;
++ptr;
switch(res[ptr]) {
case '0': str+='\0'; break;
case 'a': str+='\a'; break;
case 'b': str+='\b'; break;
case 'e': str+='\033'; break;
case 't': str+='\t'; break;
case 'n': str+='\n'; break;
case 'v': str+='\v'; break;
case 'f': str+='\f'; break;
case 'r': str+='\r'; break;
case '?': str+='\?'; break;
case '\\':str+='\\'; break;
case '\'':str+='\''; break;
case '\"':str+='\"'; break;
default: str+=res[ptr];break;
case '0': str += '\0'; break;
case 'a': str += '\a'; break;
case 'b': str += '\b'; break;
case 'e': str += '\033'; break;
case 't': str += '\t'; break;
case 'n': str += '\n'; break;
case 'v': str += '\v'; break;
case 'f': str += '\f'; break;
case 'r': str += '\r'; break;
case '?': str += '\?'; break;
case '\\':str += '\\'; break;
case '\'':str += '\''; break;
case '\"':str += '\"'; break;
default: str += res[ptr];break;
}
if (res[ptr]=='\n') {
column=0;
column = 0;
++line;
}
continue;
}
str+=res[ptr];
str += res[ptr];
}
// check if this string ends with a " or '
if (ptr++>=res.size()) {
err.err("lexer", {begin_line, begin_column, line, column, filename}, "get EOF when generating string");
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"get EOF when generating string");
return {{begin_line, begin_column, line, column, filename}, tok::str, str};
}
++column;
if (begin=='`' && str.length()!=1) {
err.err("lexer", {begin_line, begin_column, line, column, filename}, "\'`\' is used for string including one character");
// if is not utf8, 1+utf8_hdchk should be 1
if (begin=='`' && str.length()!=1+utf8_hdchk(str[0])) {
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"\'`\' is used for string including one character");
}
return {{begin_line, begin_column, line, column, filename}, tok::str, str};
}
token lexer::single_opr() {
u32 begin_line=line;
u32 begin_column=column;
std::string str(1,res[ptr]);
u32 begin_line = line;
u32 begin_column = column;
std::string str(1, res[ptr]);
++column;
tok type=get_type(str);
tok type = get_type(str);
if (type==tok::null) {
err.err("lexer", {begin_line, begin_column, line, column, filename}, "invalid operator `"+str+"`");
err.err("lexer",
{begin_line, begin_column, line, column, filename},
"invalid operator `"+str+"`");
}
++ptr;
return {{begin_line, begin_column, line, column, filename}, type, str};
}
token lexer::dots() {
u32 begin_line=line;
u32 begin_column=column;
std::string str=".";
u32 begin_line = line;
u32 begin_column = column;
std::string str = ".";
if (ptr+2<res.size() && res[ptr+1]=='.' && res[ptr+2]=='.') {
str+="..";
str += "..";
}
ptr+=str.length();
column+=str.length();
ptr += str.length();
column += str.length();
return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
}
token lexer::calc_opr() {
u32 begin_line=line;
u32 begin_column=column;
u32 begin_line = line;
u32 begin_column = column;
// get calculation operator
std::string str(1,res[ptr++]);
std::string str(1, res[ptr++]);
if (ptr<res.size() && res[ptr]=='=') {
str+=res[ptr++];
str += res[ptr++];
}
column+=str.length();
column += str.length();
return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
}
const error& lexer::scan(const std::string& file) {
line=1;
column=0;
ptr=0;
line = 1;
column = 0;
ptr = 0;
open(file);
while(ptr<res.size()) {
@ -303,7 +326,7 @@ const error& lexer::scan(const std::string& file) {
++column;
if (res[ptr++]=='\n') {
++line;
column=0;
column = 0;
}
}
if (ptr>=res.size()) {
@ -328,6 +351,6 @@ const error& lexer::scan(const std::string& file) {
}
}
toks.push_back({{line, column, line, column, filename}, tok::eof, "<eof>"});
res="";
res = "";
return err;
}

View File

@ -85,14 +85,14 @@ bool is_superh() {
}
f64 hex2f(const char* str) {
f64 ret=0;
f64 ret = 0;
for(; *str; ++str) {
if ('0'<=*str && *str<='9') {
ret=ret*16+(*str-'0');
ret = ret*16+(*str-'0');
} else if ('a'<=*str && *str<='f') {
ret=ret*16+(*str-'a'+10);
ret = ret*16+(*str-'a'+10);
} else if ('A'<=*str && *str<='F') {
ret=ret*16+(*str-'A'+10);
ret = ret*16+(*str-'A'+10);
} else {
return nan("");
}
@ -101,9 +101,9 @@ f64 hex2f(const char* str) {
}
f64 oct2f(const char* str) {
f64 ret=0;
f64 ret = 0;
while('0'<=*str && *str<'8') {
ret=ret*8+(*str++-'0');
ret = ret*8+(*str++-'0');
}
if (*str) {
return nan("");
@ -118,9 +118,9 @@ f64 oct2f(const char* str) {
// but this also makes 0.1+0.2==0.3,
// not another result that you may get in other languages.
f64 dec2f(const char* str) {
f64 ret=0,negative=1,num_pow=0;
f64 ret = 0, negative = 1, num_pow = 0;
while('0'<=*str && *str<='9') {
ret=ret*10+(*str++-'0');
ret = ret*10+(*str++-'0');
}
if (!*str) {
return ret;
@ -129,10 +129,10 @@ f64 dec2f(const char* str) {
if (!*++str) {
return nan("");
}
num_pow=0.1;
num_pow = 0.1;
while('0'<=*str && *str<='9') {
ret+=num_pow*(*str++-'0');
num_pow*=0.1;
ret += num_pow*(*str++-'0');
num_pow *= 0.1;
}
if (!*str) {
return ret;
@ -145,14 +145,14 @@ f64 dec2f(const char* str) {
return nan("");
}
if (*str=='-' || *str=='+') {
negative=(*str++=='-'? -1:1);
negative = (*str++=='-'? -1:1);
}
if (!*str) {
return nan("");
}
num_pow=0;
num_pow = 0;
while('0'<=*str && *str<='9') {
num_pow=num_pow*10+(*str++-'0');
num_pow = num_pow*10+(*str++-'0');
}
if (*str) {
return nan("");
@ -161,27 +161,27 @@ f64 dec2f(const char* str) {
}
f64 str2num(const char* str) {
bool negative=false;
f64 res=0;
bool negative = false;
f64 res = 0;
if (*str=='-' || *str=='+') {
negative=(*str++=='-');
negative = (*str++=='-');
}
if (!*str) {
return nan("");
}
if (str[0]=='0' && str[1]=='x') {
res=hex2f(str+2);
res = hex2f(str+2);
} else if (str[0]=='0' && str[1]=='o') {
res=oct2f(str+2);
res = oct2f(str+2);
} else {
res=dec2f(str);
res = dec2f(str);
}
return negative?-res:res;
return negative? -res:res;
}
i32 utf8_hdchk(const char head) {
// RFC-2279 but now we use RFC-3629 so nbytes is less than 4
const u8 c=(u8)head;
const u8 c = (u8)head;
if ((c>>5)==0x06) { // 110x xxxx (10xx xxxx)^1
return 1;
}
@ -195,36 +195,36 @@ i32 utf8_hdchk(const char head) {
}
std::string chrhex(const char c) {
const char hextbl[]="0123456789abcdef";
return {hextbl[(c&0xf0)>>4],hextbl[c&0x0f]};
const char hextbl[] = "0123456789abcdef";
return {hextbl[(c&0xf0)>>4], hextbl[c&0x0f]};
}
std::string rawstr(const std::string& str, const usize maxlen) {
std::string ret("");
for(auto i:str) {
for(auto i : str) {
// windows doesn't output unicode normally, so we output the hex
if (is_windows() && i<=0) {
ret+="\\x"+chrhex(i);
ret += "\\x"+chrhex(i);
continue;
}
switch(i) {
case '\0': ret+="\\0"; break;
case '\a': ret+="\\a"; break;
case '\b': ret+="\\b"; break;
case '\t': ret+="\\t"; break;
case '\n': ret+="\\n"; break;
case '\v': ret+="\\v"; break;
case '\f': ret+="\\f"; break;
case '\r': ret+="\\r"; break;
case '\033':ret+="\\e"; break;
case '\"': ret+="\\\"";break;
case '\'': ret+="\\\'";break;
case '\\': ret+="\\\\";break;
default: ret+=i; break;
case '\0': ret += "\\0"; break;
case '\a': ret += "\\a"; break;
case '\b': ret += "\\b"; break;
case '\t': ret += "\\t"; break;
case '\n': ret += "\\n"; break;
case '\v': ret += "\\v"; break;
case '\f': ret += "\\f"; break;
case '\r': ret += "\\r"; break;
case '\033':ret += "\\e"; break;
case '\"': ret += "\\\""; break;
case '\'': ret += "\\\'"; break;
case '\\': ret += "\\\\"; break;
default: ret += i; break;
}
}
if (maxlen && ret.length()>maxlen) {
ret=ret.substr(0,maxlen)+"...";
ret = ret.substr(0,maxlen)+"...";
}
return ret;
}

View File

@ -8,26 +8,28 @@ void vm::init(
const std::vector<std::string>& filenames,
const std::vector<std::string>& argv
) {
cnum=nums.data();
cstr=strs.data();
bytecode=code.data();
files=filenames.data();
cnum = nums.data();
cstr = strs.data();
bytecode = code.data();
files = filenames.data();
/* set canary and program counter */
ctx.pc=0;
ctx.localr=ctx.memr=nullptr;
ctx.funcr=ctx.upvalr=nil;
ctx.canary=stack+STACK_DEPTH-1; // stack[STACK_DEPTH-1]
ctx.top=stack;
ctx.stack=stack;
ctx.pc = 0;
ctx.localr = nullptr;
ctx.memr = nullptr;
ctx.funcr = nil;
ctx.upvalr = nil;
ctx.canary = stack+STACK_DEPTH-1; // stack[STACK_DEPTH-1]
ctx.top = stack;
ctx.stack = stack;
/* clear main stack */
for(u32 i=0;i<STACK_DEPTH;++i) {
stack[i]=nil;
for(u32 i = 0; i<STACK_DEPTH; ++i) {
stack[i] = nil;
}
/* init gc */
ngc.init(strs,argv);
ngc.init(strs, argv);
/* init vm globals */
auto map_instance = ngc.alloc(vm_map);
@ -35,10 +37,15 @@ void vm::init(
for(const auto& i : global) {
map_instance.map().mapper[i.first] = stack+i.second;
}
/* init vm arg */
auto arg_instance = ngc.alloc(vm_vec);
stack[global.at("arg")] = arg_instance;
arg_instance.vec().elems = ngc.env_argv;
}
void vm::valinfo(var& val) {
const nas_val* p=val.val.gcobj;
const nas_val* p = val.val.gcobj;
switch(val.type) {
case vm_none: std::clog<<"| null |";break;
case vm_ret: std::clog<<"| pc | 0x"<<std::hex
@ -49,7 +56,8 @@ void vm::valinfo(var& val) {
case vm_nil: std::clog<<"| nil |";break;
case vm_num: std::clog<<"| num | "<<val.num();break;
case vm_str: std::clog<<"| str | <0x"<<std::hex<<(u64)p
<<"> "<<rawstr(val.str(),16)<<std::dec;break;
<<"> "<<rawstr(val.str(),16)
<<std::dec;break;
case vm_func: std::clog<<"| func | <0x"<<std::hex<<(u64)p
<<"> entry:0x"<<val.func().entry
<<std::dec;break;

View File

@ -322,6 +322,19 @@ inline void vm::o_sub() {op_calc(-);}
inline void vm::o_mul() {op_calc(*);}
inline void vm::o_div() {op_calc(/);}
inline void vm::o_lnk() {
if (ctx.top[-1].type==vm_vec && ctx.top[0].type==vm_vec) {
ngc.temp = ngc.alloc(vm_vec);
for(auto i : ctx.top[-1].vec().elems) {
ngc.temp.vec().elems.push_back(i);
}
for(auto i : ctx.top[0].vec().elems) {
ngc.temp.vec().elems.push_back(i);
}
ctx.top[-1] = ngc.temp;
ngc.temp = nil;
--ctx.top;
return;
}
ctx.top[-1] = ngc.newstr(ctx.top[-1].tostr()+ctx.top[0].tostr());
--ctx.top;
}
@ -622,9 +635,12 @@ inline void vm::o_callfv() {
return;
}
auto& func = local[-1].func();
// swap funcr with local[-1]
var tmp = local[-1];
local[-1] = ctx.funcr;
ctx.funcr=tmp;
ctx.funcr = tmp;
// top-argc+lsize(local) +1(old pc) +1(old localr) +1(old upvalr)
if (ctx.top-argc+func.lsize+3>=ctx.canary) {
die("stack overflow");
@ -640,14 +656,14 @@ inline void vm::o_callfv() {
var dynamic = nil;
if (func.dpara>=0) { // load dynamic arguments
dynamic = ngc.alloc(vm_vec);
for(u32 i=psize;i<argc;++i) {
for(u32 i = psize; i<argc; ++i) {
dynamic.vec().elems.push_back(local[i]);
}
} else if (psize<argc) {
// load arguments to "arg", located at stack+1
stack[1] = ngc.alloc(vm_vec);
for(u32 i=psize;i<argc;++i) {
stack[1].vec().elems.push_back(local[i]);
dynamic = ngc.alloc(vm_vec);
for(u32 i = psize; i<argc; ++i) {
dynamic.vec().elems.push_back(local[i]);
}
}
// should reset stack top after allocating vector
@ -658,22 +674,21 @@ inline void vm::o_callfv() {
ctx.top = local+func.lsize;
u32 min_size = (std::min)(psize, argc); // avoid error in MSVC
for(u32 i=min_size;i>=1;--i) { // load arguments
for(u32 i = min_size; i>=1; --i) { // load arguments
local[i] = local[i-1];
}
local[0] = func.local[0];// load "me"
// load local scope & default arguments
for(u32 i=min_size+1;i<func.lsize;++i) {
for(u32 i = min_size+1; i<func.lsize; ++i) {
local[i] = func.local[i];
}
if (func.dpara>=0) {
local[psize+1] = dynamic;
}
local[func.dpara>=0? psize+1:func.lsize-1] = dynamic;
ctx.top[0] = ctx.upvalr;
(++ctx.top)[0] = var::addr(ctx.localr);
(++ctx.top)[0] = var::ret(ctx.pc);
ctx.pc=func.entry-1;
ctx.pc = func.entry-1;
ctx.localr = local;
ctx.upvalr = nil;
}
@ -912,9 +927,6 @@ inline void vm::o_ret() {
ctx.funcr = ctx.top[0];
ctx.top[0] = ret; // rewrite func with returned value
// reset "arg"
stack[1] = nil;
if (up.type==vm_upval) { // synchronize upvalue
auto& upval = up.upval();
auto size = func.func().lsize;

View File

@ -478,7 +478,7 @@ var os = {
# runtime gives us some functions that we could manage it manually.
var runtime = {
# command line arguments
argv: func() {return __sysargv;},
argv: func() {return globals.arg;},
gc: {
extend: func(type) {return __gcextd;}
}

View File

@ -45,12 +45,12 @@ var var_sort=func(){
j-=1;
}
vec[i]=tmp;
quick_sort_core(vec,left,i-1,cmp);
quick_sort_core(vec,i+1,right,cmp);
quick_sort_core(vec,left,i-1);
quick_sort_core(vec,i+1,right);
return nil;
}
return func(vec){
quick_sort_core(vec,0,size(vec)-1,cmp);
quick_sort_core(vec,0,size(vec)-1);
return nil;
}
}();

View File

@ -16,9 +16,22 @@ globals.test_func();
var f = func() {
println(arg);
func() {println(arg);}(114, 514, 1919, 810);
println(arg);
}
f(1, 2, 3);
var a = func(arg, b) {
println(arg, " ", b);
}
var b = func(a) {
println(a, " ", arg);
}
a(1, 2, 3, 4); # 1 2
b(1, 2, 3, 4); # 1 [2 3 4]
# command line arguments
println(arg);
println(globals.arg);

View File

@ -1,9 +1,10 @@
import.std.sort;
var vec=[];
setsize(vec, 1e4);
rand(time(0));
for(var i=0;i<1e4;i+=1)
append(vec,int(rand()*1e5));
vec[i] = int(rand()*1e5);
sort(vec);
for(var i=1;i<1e4;i+=1) {
if (vec[i]<vec[i-1]) {
@ -20,7 +21,7 @@ var test=func(n){
var ts=maketimestamp();
ts.stamp();
var_sort(a);
println("[time] ",str(n)," in ",ts.elapsedMSec()/1000," sec (value)");
println("[time] ",str(n)," in ",ts.elapsedMSec()/1000," sec (direct cmp)");
var a=[];
setsize(a,n);
@ -29,7 +30,7 @@ var test=func(n){
}
ts.stamp();
sort(a);
println("[time] ",str(n)," in ",ts.elapsedMSec()/1000," sec (lambda)");
println("[time] ",str(n)," in ",ts.elapsedMSec()/1000," sec (lambda cmp)");
}
for(var i=1000;i<1e6;i*=10){

View File

@ -1,16 +1,16 @@
# This file is written by Andy Ross, and is protected by GPLv2.0
# A no-op function used below to get this file to run. Ignore and read on...
dummyFunc = func { 1 }
var dummyFunc = func { 1 }
#
# Literal numbers can be decimal, exponential, or hex constants. All
# numbers are stored internally as IEEE double-precision values.
#
n1 = 3;
n2 = 3.14;
n3 = 6.023e23;
n3 = 0x123456;
var n1 = 3;
var n2 = 3.14;
var n3 = 6.023e23;
var n3 = 0x123456;
#
# Two identical string literals with different quotes. Double quotes
@ -19,14 +19,14 @@ n3 = 0x123456;
# whitespace like newlines). Double quotes handle the following
# C-like escapes: \n \r \t \xnn \"
#
s1 = 'Andy\'s "computer" has a C:\righteous\newstuff directory.';
s2 = "Andy's \"computer\" has a C:\\righteous\\newstuff directory.";
var s1 = 'Andy\'s "computer" has a C:\righteous\newstuff directory.';
var s2 = "Andy's \"computer\" has a C:\\righteous\\newstuff directory.";
#
# Literal lists use square brackets with a comma-separated expression
# list.
#
list1 = ["a", "b", 1, 2];
var list1 = ["a", "b", 1, 2];
#
# Literal hashes (or objects -- same thing) use curlies and colons to
@ -35,8 +35,8 @@ list1 = ["a", "b", 1, 2];
# to use symbols, lookup tables of other types will be more
# comfortable with literals.
#
hash1 = { name : "Andy", job : "Hacker" };
EnglishEspanol = { "one" : "uno", "two": "dos", "blue" : "azul" };
var hash1 = { name : "Andy", job : "Hacker" };
var EnglishEspanol = { "one" : "uno", "two": "dos", "blue" : "azul" };
#
# Both vectors and hashes use square brackets for the lookup operation:
@ -50,7 +50,7 @@ hash1["name"] == "Andy";
# (anonymous) function argument to the local "log_message" variable.
# There is no function declaration syntax in Nasal.
#
log_message = func {
var log_message = func {
print(arg[0]);
}
@ -58,10 +58,10 @@ log_message = func {
# You can also pass named arguments to a function, thus saving the
# typing and performance costs of extracting them from the arg array.
#
sqrt = dummyFunc;
dist = func(x1, y1, x2, y2) {
dx = x2-x1;
dy = y2-y1;
var sqrt = dummyFunc;
var dist = func(x1, y1, x2, y2) {
var dx = x2-x1;
var dy = y2-y1;
return sqrt(dx*dx + dy*dy);
}
dist(0,0,1,1); # == sqrt(2)
@ -71,14 +71,14 @@ dist(0,0,1,1); # == sqrt(2)
# default value must be a scalar (number, string, function, nil) and
# not a mutable composite object (list, hash).
#
read = func(bytes, flags=0) { }
var read = func(bytes, flags=0) { }
#
# Any extra arguments after the named list are placed in the "arg"
# vector as above. You can rename this to something other than "arg"
# by specifying a final argument name with an ellipsis:
#
listify = func(elements...) { return elements; }
var listify = func(elements...) { return elements; }
listify(1, 2, 3, 4); # returns a list: [1, 2, 3, 4]
#
@ -87,7 +87,7 @@ listify(1, 2, 3, 4); # returns a list: [1, 2, 3, 4]
# good practice in general, although it is not required. Note that
# this is not a "declaration", just a qualifier on the "=" operator.
#
innerFunc = func {
var innerFunc = func {
for(var dist=0; dist<100; dist += 1) {
# Does not interfere with the "dist" symbol defined above
}
@ -99,26 +99,26 @@ innerFunc = func {
# what the ?: does in C. The last semicolon in a code block is
# optional, to make this prettier.
#
abs = func(n) { if(n<0) { -n } else { n } }
var abs = func(n) { if(n<0) { -n } else { n } }
#
# But for those who don't like typing, the ternary operator works like
# you expect:
#
abs = func(n) { n < 0 ? -n : n }
var abs = func(n) { n < 0 ? -n : n }
#
# Nasal supports a "nil" value for use as a null pointer equivalent.
# It can be tested for equality, matching only other nils.
#
listNode = { data : ["what", "ever"], next : nil };
var listNode = { data : ["what", "ever"], next : nil };
#
# Nasal's binary boolean operators are "and" and "or", unlike C.
# unary not is still "!" however. They short-circuit like you expect
#
toggle = 0;
a = nil;
var toggle = 0;
var a = nil;
if(a and a.field == 42) {
toggle = !toggle; # doesn't crash when a is nil
}
@ -129,24 +129,24 @@ if(a and a.field == 42) {
# takes a local variable name as its first argument and a vector as
# its second.
#
doSomething = dummyFunc;
var doSomething = dummyFunc;
stillGoing = 0;
var stillGoing = 0;
while(stillGoing) { doSomething(); }
for(i=0; i < 3; i = i+1) {
for(var i=0; i < 3; i = i+1) {
elem = list1[i];
doSomething(elem);
}
foreach(elem; list1) { doSomething(elem) } # Shorthand for above
foreach(var elem; list1) { doSomething(elem) } # Shorthand for above
#
# There is also a "forindex", which is like foreach except that it
# assigns the index of each element, instead of the value, to the loop
# variable.
#
forindex(i; list1) { doSomething(list1[i]); }
forindex(var i; list1) { doSomething(list1[i]); }
#
# Define a class object with one method, one field and one "new"
@ -155,10 +155,10 @@ forindex(i; list1) { doSomething(list1[i]); }
# appropriately. Member functions can get their local object (the
# equivalent of the "this" pointer in C++) as the "me" variable.
#
Class1 = {};
var Class1 = {};
Class1.new = func {
obj = { parents : [Class1],
var obj = { parents : [Class1],
count : 0 };
return obj;
}
@ -168,7 +168,7 @@ Class1.getcount = func {
return me.count;
}
c = Class1.new();
var c = Class1.new();
print(c.getcount(), "\n"); # prints 1
print(c.getcount(), "\n"); # prints 2
print(c.getcount(), "\n"); # prints 3
@ -177,20 +177,20 @@ print(c.getcount(), "\n"); # prints 3
# But *set* operations always go to the local object. You can't
# corrupt a parent class via OOP operations on its instances (but you
# *can* get to it via hand-inspection of the parents arrays).
c2 = Class1.new();
c2.getcount() = func { 12345 }; # custom "derived" function!
var c2 = Class1.new();
c2.getcount = func { return 12345 }; # custom "derived" function!
print(c2.getcount(), "\n"); # prints 12345
print(c1.getcount(), "\n"); # prints 4, Class1.getcount is unchanged
print(c.getcount(), "\n"); # prints 4, Class1.getcount is unchanged
#
# This creates an identical class using alternative syntax.
#
Class2 = {
var Class2 = {
new : func {
obj = {};
obj.parents = [Class2];
obj.count = 0;
var obj = {};
obj.parents = [Class2];
obj.count = 0;
return obj;
},
getcount : func {
@ -206,7 +206,7 @@ Class2 = {
# C (although note that there is no nul termination -- get the length
# with size()):
#
string = "abcdefghijklmnopqrstuvwxyz";
var string = "abcdefghijklmnopqrstuvwxyz";
var ascii_sum = 0;
for(var i=0; i<size(string); i+=1) { ascii_sum += string[i]; }
@ -222,7 +222,7 @@ if(`©` != 169) { print("Unicode violation bug!\n"); }
# can make a mutable string either with the append operator or the
# bits.buf() function.
#
ascii_lc = func(string) {
var ascii_lc = func(string) {
var mutable = string ~ "";
for(var i=0; i<size(mutable); i+=1) {
if(mutable[i] >= `A` and mutable[i] <= `Z`) {
@ -237,14 +237,15 @@ print(ascii_lc("ABCDEFG"), "\n"); # prints "abcdefg"
# Advanced vectors: The lookup index can be negative, where -1
# indicates the last element in the vector (or string).
#
next_to_last = list1[-2];
var next_to_last = list1[-2];
#
# Remember that strings look syntactically like vectors of bytes; so
# conversely, the "~" concatenation operator works equally well to
# concatenate vectors:
#
joined_list = [1, 2, 3] ~ [4, 5, 6];
var joined_list = [1, 2, 3] ~ [4, 5, 6];
print(joined_list, "\n");
###
### Now some fun examples:
@ -254,9 +255,9 @@ joined_list = [1, 2, 3] ~ [4, 5, 6];
# Make a "inverted index" hash out of a vector that returns the index
# for each element.
#
invert = func(vec) {
hash = {};
for(i=0; i<size(vec); i = i+1) {
var invert = func(vec) {
var hash = {};
for(var i=0; i<size(vec); i = i+1) {
hash[vec[i]] = i;
}
return hash;
@ -266,16 +267,16 @@ invert = func(vec) {
# Use the return value of the above function to do an "index of"
# lookup on a vector
#
vecfind = func(vec, elem) { return invert(vec)[elem]; }
var vecfind = func(vec, elem) { return invert(vec)[elem]; }
#
# Joins its arguments with the empty string and returns a scalar.
# Note use of "~" operator to do string concatenation (Nasal's only
# funny syntax).
#
join = func {
s = "";
foreach(elem; arg) { s = s ~ elem; }
var join = func {
var s = "";
foreach(var elem; arg) { s = s ~ elem; }
return s;
}
@ -283,14 +284,16 @@ join = func {
# Labeled break/continue syntax puts the label in as an extra first
# argument to the for/while/foreach.
#
doneWithInnerLoopEarly = dummyFunc;
completelyDone = dummyFunc;
for(OUTER; i=0; i<100; i = i+1) {
for(j=0; j<100; j = j+1) {
var doneWithInnerLoopEarly = dummyFunc;
var completelyDone = dummyFunc;
# not supported now
for(#OUTER;
var i=0; i<100; i = i+1) {
for(var j=0; j<100; j = j+1) {
if(doneWithInnerLoopEarly()) {
break;
} elsif(completelyDone()) {
break OUTER;
break #OUTER;
}
}
}
@ -303,10 +306,10 @@ for(OUTER; i=0; i<100; i = i+1) {
## also makes no attempt to escape special characters in strings, which
## can break re-parsing in strange (and possibly insecure!) ways.
##
dump = func(o) {
result = "";
var dump = func(o) {
var result = "";
if(typeof(o) == "scalar") {
n = num(o);
var n = num(o);
if(n == nil) { result = result ~ '"' ~ o ~ '"'; }
else { result = result ~ o; }
} elsif(typeof(o) == "vector") {
@ -317,14 +320,14 @@ dump = func(o) {
}
result = result ~ " ]";
} elsif(typeof(o) == "hash") {
ks = keys(o);
var ks = keys(o);
result = result ~ "{ ";
if(size(o) > 0) {
k = ks[0];
var k = ks[0];
result = result ~ k ~ ":" ~ dump(o[k]);
}
for(i=1; i<size(o); i=i+1) {
k = ks[i];
var k = ks[i];
result = result ~ ", " ~ k ~ " : " ~ dump(o[k]);
}
result = result ~ " }";
@ -345,7 +348,7 @@ dump = func(o) {
# normal function definition. Oh well, every language has a syntactic
# quirk or two...)
#
a = (func(n){ n + 1 })(232); # "a" now equals 233
var a = (func(n){ n + 1 })(232); # "a" now equals 233
#
# Functional programming B. All expressions have a value, the last
@ -354,7 +357,7 @@ a = (func(n){ n + 1 })(232); # "a" now equals 233
# (assignment, duh) have side effects. e.g. The "if" expression works
# both for code flow and as the ?: expression in C/C++.
#
factorial = func(n) { if(n == 0) { 1 }
var factorial = func(n) { if(n == 0) { 1 }
else { n * factorial(n-1) } }
print(factorial(10), "\n");
@ -364,8 +367,8 @@ print(factorial(10), "\n");
# local variables in the outer scope even after their creator has
# returned.
#
getcounter = func { count = 0; return func { count = count + 1 } }
mycounter = getcounter();
var getcounter = func { var count = 0; return func { count = count + 1 } }
var mycounter = getcounter();
print(mycounter(), "\n"); # prints 1
print(mycounter(), "\n"); # prints 2
print(mycounter(), "\n"); # prints 3

View File

@ -229,3 +229,5 @@ for(var a=0;a<16;a+=1) {
println("temp^=0x"~h[a]~" -> 0x",h[temp^=a]," temp&=0x"~h[a]~" -> 0x",h[temp&=a]," temp|=0x"~h[a]~" -> 0x",h[temp|=a]);
}
}
print([0, 1, 2]~[3, 4, 5], "\n");