🐛 complete function of arg in all scopes

This commit is contained in:
ValKmjolnir 2023-07-09 16:21:09 +08:00
parent 3509655424
commit 7e72661332
12 changed files with 291 additions and 306 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 varibale 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

@ -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,16 @@ 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)}
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 +1104,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

@ -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

@ -622,9 +622,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 +643,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 +661,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 +914,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

@ -16,9 +16,12 @@ 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);
# command line arguments
println(arg); println(arg);
println(globals.arg); println(globals.arg);

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,20 +177,20 @@ 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;
}, },
getcount : func { getcount : func {
@ -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,14 @@ 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];
### ###
### Now some fun examples: ### Now some fun examples:
@ -254,9 +254,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 +266,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 +283,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 +305,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 +319,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 +347,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 +356,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 +366,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