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> <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. 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: In Andy's interpreter:
@ -794,32 +796,6 @@ code: undefined symbol "i"
</details> </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__ ## __Trace Back Info__
![stackoverflow](./doc/gif/stackoverflow.gif) ![stackoverflow](./doc/gif/stackoverflow.gif)

View File

@ -735,6 +735,7 @@ dylib.dlclose(dlhandle.lib);
<details><summary>必须用 var 定义变量</summary> <details><summary>必须用 var 定义变量</summary>
这个解释器使用了更加严格的语法检查来保证你可以更轻松地debug。这是非常有必要的严格否则debug会非常痛苦。 这个解释器使用了更加严格的语法检查来保证你可以更轻松地debug。这是非常有必要的严格否则debug会非常痛苦。
同样的flightgear 内置的 nasal 解释器也采取了类似的措施,所以使用变量前务必用 `var` 先进行声明。
在Andy的解释器中: 在Andy的解释器中:
@ -762,29 +763,6 @@ code: undefined symbol "i"
``` ```
</details> </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) ![stackoverflow](../doc/gif/stackoverflow.gif)

View File

@ -1,5 +1,23 @@
STD=c++17 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=\ NASAL_OBJECT=\
build/nasal_err.o\ build/nasal_err.o\
build/nasal_ast.o\ build/nasal_ast.o\
@ -29,55 +47,96 @@ nasal.exe: $(NASAL_OBJECT) | build
build: build:
@ if [ ! -d build ]; then mkdir build; fi @ 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 . $(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 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 . $(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 . $(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 . $(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 . $(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 . $(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 . $(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 . $(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 . $(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 . $(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 . $(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 . $(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 . $(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 . $(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 . $(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 . $(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 . $(CXX) -std=$(STD) -c -O3 src/nasal_dbg.cpp -fno-exceptions -fPIC -o build/nasal_dbg.o -I .
.PHONY: clean .PHONY: clean

View File

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

View File

@ -145,7 +145,7 @@ void execute(
// get running time // get running time
if (cmd&VM_TIME) { if (cmd&VM_TIME) {
f64 tm = (clk::now()-start).count()*1.0/den; 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 usize = std::size_t;
using f64 = double; using f64 = double;
const u32 STACK_DEPTH=4096; const u32 STACK_DEPTH = 4096;
f64 hex2f(const char*); f64 hex2f(const char*);
f64 oct2f(const char*); f64 oct2f(const char*);

View File

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

View File

@ -124,7 +124,6 @@ var builtin_coyield(var*, gc&);
var builtin_costatus(var*, gc&); var builtin_costatus(var*, gc&);
var builtin_corun(var*, gc&); var builtin_corun(var*, gc&);
var builtin_millisec(var*, gc&); var builtin_millisec(var*, gc&);
var builtin_sysargv(var*, gc&);
var builtin_gcextend(var*, gc&); var builtin_gcextend(var*, gc&);
var builtin_logtime(var*, gc&); var builtin_logtime(var*, gc&);
var builtin_ghosttype(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 // search symbols first, must use after loading parameters
// or the location of symbols will change and cause fatal error // or the location of symbols will change and cause fatal error
find_symbol(block); 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); in_iterloop.push(0);
block_gen(block); block_gen(block);
in_iterloop.pop(); 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 special symbol globals, which is a hash stores all global variables
add_symbol("globals"); add_symbol("globals");
// add special symbol arg here, which is used to store function arguments // add special symbol arg here, which is used to store command line args
// 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_symbol("arg"); add_symbol("arg");
find_symbol(parse.tree()); // search symbols first 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> dbg::parse(const std::string& cmd) {
std::vector<std::string> res; std::vector<std::string> res;
usize last=0; usize last = 0, pos = cmd.find(" ", 0);
usize pos=cmd.find(" ", 0);
while(pos!=std::string::npos) { while(pos!=std::string::npos) {
if (pos>last) { if (pos>last) {
res.push_back(cmd.substr(last, pos-last)); res.push_back(cmd.substr(last, pos-last));
} }
last=pos+1; last = pos+1;
pos=cmd.find(" ", last); pos = cmd.find(" ", last);
} }
if (last<cmd.length()) { if (last<cmd.length()) {
res.push_back(cmd.substr(last)); 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 { 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]) { if (filename==files[i]) {
return i; return i;
} }
@ -28,76 +27,78 @@ u16 dbg::file_index(const std::string& filename) const {
void dbg::err() { void dbg::err() {
std::cerr std::cerr
<<"incorrect command\n" << "incorrect command\n"
<<"input \'h\' to get help\n"; << "input \'h\' to get help\n";
} }
void dbg::help() { void dbg::help() {
std::clog std::clog
<<"<option>\n" << "<option>\n"
<<" h, help | get help\n" << " h, help | get help\n"
<<" bt, backtrace | get function call trace\n" << " bt, backtrace | get function call trace\n"
<<" c, continue | run program until break point or exit\n" << " c, continue | run program until break point or exit\n"
<<" f, file | see all the compiled files\n" << " f, file | see all the compiled files\n"
<<" g, global | see global values\n" << " g, global | see global values\n"
<<" l, local | see local values\n" << " l, local | see local values\n"
<<" u, upval | see upvalue\n" << " u, upval | see upvalue\n"
<<" r, register | show vm register detail\n" << " r, register | show vm register detail\n"
<<" a, all | show global,local and upvalue\n" << " a, all | show global,local and upvalue\n"
<<" n, next | execute next bytecode\n" << " n, next | execute next bytecode\n"
<<" q, exit | exit debugger\n" << " q, exit | exit debugger\n"
<<"<option> <filename> <line>\n" << "<option> <filename> <line>\n"
<<" bk, break | set break point\n"; << " bk, break | set break point\n";
} }
void dbg::list_file() const { void dbg::list_file() const {
for(usize i=0; i<fsize; ++i) { for(usize i = 0; i<fsize; ++i) {
std::clog<<"["<<i<<"] "<<files[i]<<"\n"; std::clog << "[" << i << "] " << files[i] << "\n";
} }
} }
void dbg::call_sort(const u64* arr) const { void dbg::call_sort(const u64* arr) const {
typedef std::pair<u32,u64> op; typedef std::pair<u32,u64> op;
std::vector<op> opcall; std::vector<op> opcall;
u64 total=0; u64 total = 0;
for(u32 i=0;i<op_ret+1;++i) { for(u32 i = 0; i<op_ret+1; ++i) {
total+=arr[i]; total += arr[i];
opcall.push_back({i, arr[i]}); opcall.push_back({i, arr[i]});
} }
std::sort(opcall.begin(), opcall.end(), std::sort(opcall.begin(), opcall.end(),
[](const op& a, const op& b) {return a.second>b.second;} [](const op& a, const op& b) {return a.second>b.second;}
); );
std::clog<<"\noperands call info (<1% ignored)\n"; std::clog << "\noperands call info (<1% ignored)\n";
for(auto& i:opcall) { for(auto& i : opcall) {
u64 rate=i.second*100/total; u64 rate = i.second*100/total;
if (!rate) { if (!rate) {
break; 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() { void dbg::step_info() {
u32 line=bytecode[ctx.pc].line==0?0:bytecode[ctx.pc].line-1; u32 line = bytecode[ctx.pc].line==0? 0:bytecode[ctx.pc].line-1;
u32 begin=(line>>3)==0?0:((line>>3)<<3); u32 begin = (line>>3)==0? 0:((line>>3)<<3);
u32 end=(1+(line>>3))<<3; u32 end = (1+(line>>3))<<3;
src.load(files[bytecode[ctx.pc].fidx]); src.load(files[bytecode[ctx.pc].fidx]);
std::clog<<"\nsource code:\n"; std::clog << "\nsource code:\n";
for(u32 i=begin;i<end && i<src.size();++i) { 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 << (i==line? back_white:reset);
std::clog << (i==line? "--> ":" ") << src[i] << reset << "\n";
} }
begin=(ctx.pc>>3)==0?0:((ctx.pc>>3)<<3); begin = (ctx.pc>>3)==0? 0:((ctx.pc>>3)<<3);
end=(1+(ctx.pc>>3))<<3; end = (1+(ctx.pc>>3))<<3;
codestream::set(cnum, cstr, files); codestream::set(cnum, cstr, files);
std::clog<<"next bytecode:\n"; std::clog << "next bytecode:\n";
for(u32 i=begin;i<end && bytecode[i].op!=op_exit;++i) { for(u32 i = begin; i<end && bytecode[i].op!=op_exit; ++i) {
std::clog std::clog
<<(i==ctx.pc?back_white:reset) << (i==ctx.pc? back_white:reset)
<<(i==ctx.pc?"--> ":" ") << (i==ctx.pc? "--> ":" ")
<<codestream(bytecode[i], i) << codestream(bytecode[i], i)
<<reset<<"\n"; << reset << "\n";
} }
stackinfo(10); stackinfo(10);
} }
@ -114,11 +115,11 @@ void dbg::interact() {
return; return;
} }
next=false; next = false;
std::string cmd; std::string cmd;
step_info(); step_info();
while(true) { while(true) {
std::clog<<">> "; std::clog << ">> ";
std::getline(std::cin, cmd); std::getline(std::cin, cmd);
auto res=parse(cmd); auto res=parse(cmd);
if (res.size()==0) { if (res.size()==0) {
@ -134,22 +135,22 @@ void dbg::interact() {
case dbg_cmd::cmd_upval: ustate(); break; case dbg_cmd::cmd_upval: ustate(); break;
case dbg_cmd::cmd_register: reginfo(); break; case dbg_cmd::cmd_register: reginfo(); break;
case dbg_cmd::cmd_show_all: detail(); 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); case dbg_cmd::cmd_exit: std::exit(0);
default: err(); break; default: err(); break;
} }
} else if (res.size()==3 && } else if (res.size()==3 &&
get_cmd_type(res[0])==dbg_cmd::cmd_break_point) { 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) { if (bk_fidx==65535) {
std::clog<<"cannot find file named `"<<res[1]<<"`\n"; std::clog << "cannot find file named `" << res[1] << "`\n";
continue; continue;
} }
i32 tmp=atoi(res[2].c_str()); i32 tmp = atoi(res[2].c_str());
if (tmp<=0) { if (tmp<=0) {
std::clog<<"incorrect line number `"<<res[2]<<"`\n"; std::clog << "incorrect line number `" << res[2] << "`\n";
} else { } else {
bk_line=tmp; bk_line = tmp;
} }
} else { } else {
err(); err();
@ -161,17 +162,17 @@ void dbg::run(
const codegen& gen, const codegen& gen,
const linker& linker, const linker& linker,
const std::vector<std::string>& argv) { const std::vector<std::string>& argv) {
verbose=true; verbose = true;
fsize=linker.filelist().size(); fsize = linker.filelist().size();
init(gen.strs(), init(gen.strs(),
gen.nums(), gen.nums(),
gen.codes(), gen.codes(),
gen.globals(), gen.globals(),
linker.filelist(), linker.filelist(),
argv); argv);
u64 count[op_ret+1]={0}; u64 count[op_ret+1] = {0};
typedef void (dbg::*nafunc)(); typedef void (dbg::*nafunc)();
const nafunc oprs[]={ const nafunc oprs[] = {
nullptr, &dbg::o_intg, nullptr, &dbg::o_intg,
&dbg::o_intl, &dbg::o_loadg, &dbg::o_intl, &dbg::o_loadg,
&dbg::o_loadl, &dbg::o_loadu, &dbg::o_loadl, &dbg::o_loadu,
@ -218,7 +219,7 @@ void dbg::run(
&dbg::o_ret &dbg::o_ret
}; };
std::vector<u32> code; std::vector<u32> code;
for(auto& i:gen.codes()) { for(auto& i : gen.codes()) {
code.push_back(i.op); code.push_back(i.op);
imm.push_back(i.num); imm.push_back(i.num);
} }

View File

@ -13,7 +13,7 @@ std::ostream& back_white(std::ostream& s) {
#ifdef _WIN32 #ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0xf0); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0xf0);
#else #else
s<<"\033[7m"; s << "\033[7m";
#endif #endif
return s; return s;
} }
@ -22,7 +22,7 @@ std::ostream& red(std::ostream& s) {
#ifdef _WIN32 #ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0c); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0c);
#else #else
s<<"\033[91;1m"; s << "\033[91;1m";
#endif #endif
return s; return s;
} }
@ -31,7 +31,7 @@ std::ostream& cyan(std::ostream& s) {
#ifdef _WIN32 #ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x03); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x03);
#else #else
s<<"\033[36;1m"; s << "\033[36;1m";
#endif #endif
return s; return s;
} }
@ -40,7 +40,7 @@ std::ostream& orange(std::ostream& s) {
#ifdef _WIN32 #ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0e); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0e);
#else #else
s<<"\033[93;1m"; s << "\033[93;1m";
#endif #endif
return s; return s;
} }
@ -49,7 +49,7 @@ std::ostream& white(std::ostream& s) {
#ifdef _WIN32 #ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0f); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0f);
#else #else
s<<"\033[0m\033[1m"; s << "\033[0m\033[1m";
#endif #endif
return s; return s;
} }
@ -58,7 +58,7 @@ std::ostream& reset(std::ostream& s) {
#ifdef _WIN32 #ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), reset_ter_color.scr.wAttributes); SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), reset_ter_color.scr.wAttributes);
#else #else
s<<"\033[0m"; s << "\033[0m";
#endif #endif
return s; return s;
} }
@ -73,7 +73,7 @@ void flstream::load(const std::string& f) {
res.clear(); res.clear();
std::ifstream in(f, std::ios::binary); std::ifstream in(f, std::ios::binary);
if (in.fail()) { if (in.fail()) {
std::cerr<<red<<"src: "<<reset<<"cannot open <"<<f<<">\n"; std::cerr << red << "src: " << reset << "cannot open <" << f << ">\n";
std::exit(1); 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) { 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()) { if (file.length()) {
std::cerr<<cyan<<" --> "<<red<<file<<reset<<"\n\n"; std::cerr << cyan << " --> " << red << file << reset << "\n\n";
} else { } else {
std::cerr<<reset<<"\n"; std::cerr << reset << "\n";
} }
std::exit(1); std::exit(1);
} }
void error::err(const std::string& stage, const std::string& info) { void error::err(const std::string& stage, const std::string& info) {
++cnt; ++cnt;
std::cerr<<red<<stage<<": "<<white<<info<<reset<<"\n"; std::cerr << red << stage << ": " << white << info << reset << "\n";
if (file.length()) { if (file.length()) {
std::cerr<<cyan<<" --> "<<red<<file<<reset<<"\n\n"; std::cerr << cyan << " --> " << red << file << reset << "\n\n";
} else { } else {
std::cerr<<reset<<"\n"; std::cerr << reset << "\n";
} }
} }
@ -112,8 +112,9 @@ void error::err(
++cnt; ++cnt;
std::cerr std::cerr
<<red<<stage<<": "<<white<<info<<reset<<"\n"<<cyan<<" --> " << red << stage << ": " << white << info << reset << "\n" << cyan << " --> "
<<red<<loc.file<<":"<<loc.begin_line<<":"<<loc.begin_column+1<<reset<<"\n"; << red << loc.file << ":" << loc.begin_line << ":" << loc.begin_column+1
<< reset << "\n";
const usize maxlen = std::to_string(loc.end_line).length(); const usize maxlen = std::to_string(loc.end_line).length();
const std::string iden = identation(maxlen); const std::string iden = identation(maxlen);
@ -125,7 +126,8 @@ void error::err(
if (loc.begin_line<line && line<loc.end_line) { if (loc.begin_line<line && line<loc.end_line) {
if (line==loc.begin_line+1) { 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; continue;
} }
@ -136,37 +138,37 @@ void error::err(
} }
const std::string& code=res[line-1]; 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 // output underline
std::cerr<<cyan<<iden<<" | "<<reset; std::cerr << cyan << iden << " | " << reset;
if (loc.begin_line==loc.end_line) { if (loc.begin_line==loc.end_line) {
for(u32 i=0; i<loc.begin_column; ++i) { for(u32 i = 0; i<loc.begin_column; ++i) {
std::cerr<<char(" \t"[code[i]=='\t']); std::cerr << char(" \t"[code[i]=='\t']);
} }
for(u32 i=loc.begin_column ;i<loc.end_column; ++i) { for(u32 i = loc.begin_column ;i<loc.end_column; ++i) {
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^")<<reset; std::cerr << red << (code[i]=='\t'? "^^^^":"^") << reset;
} }
} else if (line==loc.begin_line) { } else if (line==loc.begin_line) {
for(u32 i=0; i<loc.begin_column; ++i) { for(u32 i = 0; i<loc.begin_column; ++i) {
std::cerr<<char(" \t"[code[i]=='\t']); std::cerr << char(" \t"[code[i]=='\t']);
} }
for(u32 i=loc.begin_column; i<code.size(); ++i) { for(u32 i = loc.begin_column; i<code.size(); ++i) {
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^")<<reset; std::cerr << red << (code[i]=='\t'? "^^^^":"^") << reset;
} }
} else if (loc.begin_line<line && line<loc.end_line) { } else if (loc.begin_line<line && line<loc.end_line) {
for(u32 i=0; i<code.size(); ++i) { for(u32 i = 0; i<code.size(); ++i) {
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^"); std::cerr << red << (code[i]=='\t'? "^^^^":"^");
} }
} else { } else {
for(u32 i=0; i<loc.end_column; ++i) { for(u32 i = 0; i<loc.end_column; ++i) {
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^"); std::cerr << red << (code[i]=='\t'? "^^^^":"^");
} }
} }
if (line==loc.end_line) { if (line==loc.end_line) {
std::cerr<<reset; std::cerr << reset;
} else { } 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; 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() { void gc::mark() {
std::vector<var> bfs; std::vector<var> bfs;
mark_context(bfs); mark_context(bfs);
@ -389,14 +406,18 @@ void gc::mark_var(std::vector<var>& bfs_queue, var& value) {
void gc::mark_vec(std::vector<var>& bfs_queue, nas_vec& vec) { void gc::mark_vec(std::vector<var>& bfs_queue, nas_vec& vec) {
for(auto& i : vec.elems) { for(auto& i : vec.elems) {
if (i.type>vm_num) {
bfs_queue.push_back(i); bfs_queue.push_back(i);
} }
}
} }
void gc::mark_hash(std::vector<var>& bfs_queue, nas_hash& hash) { void gc::mark_hash(std::vector<var>& bfs_queue, nas_hash& hash) {
for(auto& i : hash.elems) { for(auto& i : hash.elems) {
if (i.second.type>vm_num) {
bfs_queue.push_back(i.second); bfs_queue.push_back(i.second);
} }
}
} }
void gc::mark_func(std::vector<var>& bfs_queue, nas_func& function) { void gc::mark_func(std::vector<var>& bfs_queue, nas_func& function) {
@ -502,24 +523,39 @@ void gc::clear() {
env_argv.clear(); env_argv.clear();
} }
void gc::info() { void gc::info() const {
using std::left; using std::left;
using std::setw; using std::setw;
using std::setfill; 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[] = { const char* name[] = {
"string ", "string",
"vector ", "vector",
"hashmap ", "hashmap",
"function ", "function",
"upvalue ", "upvalue",
"object ", "object",
"coroutine", "coroutine",
"mapper " "mapper",
nullptr
}; };
usize indent = 0; usize indent = 0, len = 0;
for(u8 i = 0; i<gc_type_size; ++i) { for(usize i = 0; used_table_name[i]; ++i) {
usize len = 0; 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(); len = std::to_string(gcnt[i]).length();
indent = indent<len? len:indent; indent = indent<len? len:indent;
len = std::to_string(acnt[i]).length(); len = std::to_string(acnt[i]).length();
@ -527,51 +563,60 @@ void gc::info() {
len = std::to_string(size[i]).length(); len = std::to_string(size[i]).length();
indent = indent<len? len:indent; 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; double total = 0;
std::clog << "\ngc info (gc count|alloc count|memory size)\n";
for(u8 i = 0; i<gc_type_size; ++i) { for(u8 i = 0; i<gc_type_size; ++i) {
if (!gcnt[i] && !acnt[i] && !size[i]) { if (!gcnt[i] && !acnt[i] && !size[i]) {
continue; continue;
} }
total += gcnt[i]; 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(' ') << gcnt[i];
std::clog << " | " << left << setw(indent) << setfill(' ') << acnt[i]; std::clog << " | " << left << setw(indent) << setfill(' ') << acnt[i];
std::clog << " | " << left << setw(indent) << setfill(' ') << size[i]; std::clog << " | " << left << setw(indent) << setfill(' ') << size[i];
std::clog << "\n"; std::clog << "\n";
} }
std::clog << indent_string << "\n";
auto den = std::chrono::high_resolution_clock::duration::period::den; auto den = std::chrono::high_resolution_clock::duration::period::den;
std::clog << " gc time | " << worktime*1.0/den*1000 << " ms\n"; std::clog << " " << left << setw(indent) << setfill(' ') << "detail";
if (total) { std::clog << " | " << left << setw(indent) << setfill(' ') << "time spend";
std::clog << " avg time | " << worktime*1.0/den*1000/total << " ms\n"; std::clog << " | " << left << setw(indent) << setfill('x') << "x";
std::clog << " max gc | " << max_time*1.0/den*1000 << " ms\n"; std::clog << " | " << left << setw(indent) << setfill('x') << "x";
std::clog << " max mark | " << max_mark_time*1.0/den*1000 << " ms\n"; std::clog << "\n" << indent_string << "\n";
std::clog << " max sweep | " << max_sweep_time*1.0/den*1000 << " ms\n";
} std::clog << " " << left << setw(indent) << setfill(' ') << "gc time";
std::clog<<"\n"; 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) { var gc::alloc(u8 type) {
using clk = std::chrono::high_resolution_clock;
const u8 index = type-vm_str; const u8 index = type-vm_str;
++acnt[index]; ++acnt[index];
if (unused[index].empty()) { if (unused[index].empty()) {
++gcnt[index]; ++gcnt[index];
auto begin = clk::now(); do_mark_sweep();
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;
} }
if (unused[index].empty()) { if (unused[index].empty()) {
extend(type); extend(type);

View File

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

View File

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

View File

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

View File

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

View File

@ -8,26 +8,28 @@ void vm::init(
const std::vector<std::string>& filenames, const std::vector<std::string>& filenames,
const std::vector<std::string>& argv const std::vector<std::string>& argv
) { ) {
cnum=nums.data(); cnum = nums.data();
cstr=strs.data(); cstr = strs.data();
bytecode=code.data(); bytecode = code.data();
files=filenames.data(); files = filenames.data();
/* set canary and program counter */ /* set canary and program counter */
ctx.pc=0; ctx.pc = 0;
ctx.localr=ctx.memr=nullptr; ctx.localr = nullptr;
ctx.funcr=ctx.upvalr=nil; ctx.memr = nullptr;
ctx.canary=stack+STACK_DEPTH-1; // stack[STACK_DEPTH-1] ctx.funcr = nil;
ctx.top=stack; ctx.upvalr = nil;
ctx.stack=stack; ctx.canary = stack+STACK_DEPTH-1; // stack[STACK_DEPTH-1]
ctx.top = stack;
ctx.stack = stack;
/* clear main stack */ /* clear main stack */
for(u32 i=0;i<STACK_DEPTH;++i) { for(u32 i = 0; i<STACK_DEPTH; ++i) {
stack[i]=nil; stack[i] = nil;
} }
/* init gc */ /* init gc */
ngc.init(strs,argv); ngc.init(strs, argv);
/* init vm globals */ /* init vm globals */
auto map_instance = ngc.alloc(vm_map); auto map_instance = ngc.alloc(vm_map);
@ -35,10 +37,15 @@ void vm::init(
for(const auto& i : global) { for(const auto& i : global) {
map_instance.map().mapper[i.first] = stack+i.second; 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) { void vm::valinfo(var& val) {
const nas_val* p=val.val.gcobj; const nas_val* p = val.val.gcobj;
switch(val.type) { switch(val.type) {
case vm_none: std::clog<<"| null |";break; case vm_none: std::clog<<"| null |";break;
case vm_ret: std::clog<<"| pc | 0x"<<std::hex 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_nil: std::clog<<"| nil |";break;
case vm_num: std::clog<<"| num | "<<val.num();break; case vm_num: std::clog<<"| num | "<<val.num();break;
case vm_str: std::clog<<"| str | <0x"<<std::hex<<(u64)p 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 case vm_func: std::clog<<"| func | <0x"<<std::hex<<(u64)p
<<"> entry:0x"<<val.func().entry <<"> entry:0x"<<val.func().entry
<<std::dec;break; <<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_mul() {op_calc(*);}
inline void vm::o_div() {op_calc(/);} inline void vm::o_div() {op_calc(/);}
inline void vm::o_lnk() { 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[-1] = ngc.newstr(ctx.top[-1].tostr()+ctx.top[0].tostr());
--ctx.top; --ctx.top;
} }
@ -622,9 +635,12 @@ inline void vm::o_callfv() {
return; return;
} }
auto& func = local[-1].func(); auto& func = local[-1].func();
// swap funcr with local[-1]
var tmp = local[-1]; var tmp = local[-1];
local[-1] = ctx.funcr; local[-1] = ctx.funcr;
ctx.funcr=tmp; ctx.funcr = tmp;
// top-argc+lsize(local) +1(old pc) +1(old localr) +1(old upvalr) // top-argc+lsize(local) +1(old pc) +1(old localr) +1(old upvalr)
if (ctx.top-argc+func.lsize+3>=ctx.canary) { if (ctx.top-argc+func.lsize+3>=ctx.canary) {
die("stack overflow"); die("stack overflow");
@ -640,14 +656,14 @@ inline void vm::o_callfv() {
var dynamic = nil; var dynamic = nil;
if (func.dpara>=0) { // load dynamic arguments if (func.dpara>=0) { // load dynamic arguments
dynamic = ngc.alloc(vm_vec); 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]); dynamic.vec().elems.push_back(local[i]);
} }
} else if (psize<argc) { } else if (psize<argc) {
// load arguments to "arg", located at stack+1 // load arguments to "arg", located at stack+1
stack[1] = ngc.alloc(vm_vec); dynamic = ngc.alloc(vm_vec);
for(u32 i=psize;i<argc;++i) { for(u32 i = psize; i<argc; ++i) {
stack[1].vec().elems.push_back(local[i]); dynamic.vec().elems.push_back(local[i]);
} }
} }
// should reset stack top after allocating vector // should reset stack top after allocating vector
@ -658,22 +674,21 @@ inline void vm::o_callfv() {
ctx.top = local+func.lsize; ctx.top = local+func.lsize;
u32 min_size = (std::min)(psize, argc); // avoid error in MSVC 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[i] = local[i-1];
} }
local[0] = func.local[0];// load "me" local[0] = func.local[0];// load "me"
// load local scope & default arguments // 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]; local[i] = func.local[i];
} }
if (func.dpara>=0) { local[func.dpara>=0? psize+1:func.lsize-1] = dynamic;
local[psize+1] = dynamic;
}
ctx.top[0] = ctx.upvalr; ctx.top[0] = ctx.upvalr;
(++ctx.top)[0] = var::addr(ctx.localr); (++ctx.top)[0] = var::addr(ctx.localr);
(++ctx.top)[0] = var::ret(ctx.pc); (++ctx.top)[0] = var::ret(ctx.pc);
ctx.pc=func.entry-1; ctx.pc = func.entry-1;
ctx.localr = local; ctx.localr = local;
ctx.upvalr = nil; ctx.upvalr = nil;
} }
@ -912,9 +927,6 @@ inline void vm::o_ret() {
ctx.funcr = ctx.top[0]; ctx.funcr = ctx.top[0];
ctx.top[0] = ret; // rewrite func with returned value ctx.top[0] = ret; // rewrite func with returned value
// reset "arg"
stack[1] = nil;
if (up.type==vm_upval) { // synchronize upvalue if (up.type==vm_upval) { // synchronize upvalue
auto& upval = up.upval(); auto& upval = up.upval();
auto size = func.func().lsize; auto size = func.func().lsize;

View File

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

View File

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

View File

@ -16,9 +16,22 @@ globals.test_func();
var f = func() { var f = func() {
println(arg); println(arg);
func() {println(arg);}(114, 514, 1919, 810);
println(arg);
} }
f(1, 2, 3); 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(arg);
println(globals.arg); println(globals.arg);

View File

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