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

@ -29,7 +29,7 @@ var quick_fib(var* args, usize size, gc* ngc) {
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;
@ -71,7 +71,8 @@ var print_new_ghost(var* args, usize size, gc* ngc) {
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;
} }

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,8 +2,7 @@
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));
@ -73,7 +72,8 @@ void dbg::call_sort(const u64* arr) const {
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';
} }
@ -85,7 +85,8 @@ void dbg::step_info() {
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);

View File

@ -113,7 +113,8 @@ void error::err(
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;
} }

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,15 +406,19 @@ 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) {
for(auto& i : function.local) { for(auto& i : function.local) {
@ -502,10 +523,17 @@ 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",
@ -514,12 +542,20 @@ void gc::info() {
"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,10 +1,10 @@
#include "nasal_import.h" #include "nasal_import.h"
linker::linker(error& e): show_path(false), lib_loaded(false), err(e) { linker::linker(error& e):
show_path(false), lib_loaded(false), err(e) {
char sep = is_windows()? ';':':'; char sep = is_windows()? ';':':';
std::string PATH = getenv("PATH"); std::string PATH = getenv("PATH");
usize last=0; usize last = 0, pos = PATH.find(sep, 0);
usize 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()) {

View File

@ -55,7 +55,9 @@ 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");
} }
@ -109,7 +111,9 @@ std::string lexer::utf8_gen() {
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;
@ -131,7 +135,9 @@ token lexer::id_gen() {
} }
} }
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() {
@ -145,8 +151,11 @@ token lexer::num_gen() {
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
@ -162,7 +171,9 @@ token lexer::num_gen() {
} }
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};
} }
@ -180,7 +191,9 @@ token lexer::num_gen() {
// "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"};
} }
} }
@ -195,7 +208,9 @@ token lexer::num_gen() {
// "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"};
} }
} }
@ -244,12 +259,18 @@ token lexer::str_gen() {
} }
// 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};
} }
@ -261,7 +282,9 @@ token lexer::single_opr() {
++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};

View File

@ -15,8 +15,10 @@ void vm::init(
/* 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.funcr = nil;
ctx.upvalr = nil;
ctx.canary = stack+STACK_DEPTH-1; // stack[STACK_DEPTH-1] ctx.canary = stack+STACK_DEPTH-1; // stack[STACK_DEPTH-1]
ctx.top = stack; ctx.top = stack;
ctx.stack = stack; ctx.stack = stack;
@ -35,6 +37,11 @@ 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) {
@ -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");
@ -645,9 +661,9 @@ inline void vm::o_callfv() {
} }
} 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
@ -662,13 +678,12 @@ inline void vm::o_callfv() {
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);
@ -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");