From 7e72661332bd7e0e015bd4b830622f484fcb9108 Mon Sep 17 00:00:00 2001 From: ValKmjolnir Date: Sun, 9 Jul 2023 16:21:09 +0800 Subject: [PATCH] :bug: complete function of arg in all scopes --- README.md | 28 +---- doc/README_zh.md | 24 +---- src/nasal_builtin.cpp | 7 -- src/nasal_builtin.h | 1 - src/nasal_codegen.cpp | 18 ++-- src/nasal_lexer.cpp | 239 +++++++++++++++++++++++------------------- src/nasal_misc.cpp | 80 +++++++------- src/nasal_vm.cpp | 38 ++++--- src/nasal_vm.h | 27 +++-- std/lib.nas | 2 +- test/globals_test.nas | 3 + test/sample.nas | 130 ++++++++++++----------- 12 files changed, 291 insertions(+), 306 deletions(-) diff --git a/README.md b/README.md index 740163d..e151aff 100644 --- a/README.md +++ b/README.md @@ -761,6 +761,8 @@ If get this, Congratulations!
Must use `var` to define variables 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: @@ -794,32 +796,6 @@ code: undefined symbol "i"
-
Default dynamic arguments not supported - -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" -``` - -
- ## __Trace Back Info__ ![stackoverflow](./doc/gif/stackoverflow.gif) diff --git a/doc/README_zh.md b/doc/README_zh.md index a721005..90ee530 100644 --- a/doc/README_zh.md +++ b/doc/README_zh.md @@ -735,6 +735,7 @@ dylib.dlclose(dlhandle.lib);
必须用 var 定义变量 这个解释器使用了更加严格的语法检查来保证你可以更轻松地debug。这是非常有必要的严格,否则debug会非常痛苦。 +同样的,flightgear 内置的 nasal 解释器也采取了类似的措施,所以使用变量前务必用 `var` 先进行声明。 在Andy的解释器中: @@ -762,29 +763,6 @@ code: undefined symbol "i" ```
-
默认不定长参数 - -这个解释器在运行时,函数不会将超出参数表的那部分不定长参数放到默认的`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" -``` - -
- ## __堆栈追踪信息__ ![stackoverflow](../doc/gif/stackoverflow.gif) diff --git a/src/nasal_builtin.cpp b/src/nasal_builtin.cpp index 3f0326f..a097810 100644 --- a/src/nasal_builtin.cpp +++ b/src/nasal_builtin.cpp @@ -1210,12 +1210,6 @@ var builtin_millisec(var* local, gc& ngc) { return var::num(res); } -var builtin_sysargv(var* local, gc& ngc) { - var res = ngc.alloc(vm_vec); - res.vec().elems = ngc.env_argv; - return res; -} - var builtin_gcextend(var* local, gc& ngc) { var type = local[1]; if (type.type!=vm_str) { @@ -1356,7 +1350,6 @@ nasal_builtin_table builtin[] = { {"__costatus", builtin_costatus}, {"__corun", builtin_corun}, {"__millisec", builtin_millisec}, - {"__sysargv", builtin_sysargv}, {"__gcextd", builtin_gcextend}, {"__logtime", builtin_logtime}, {"__ghosttype", builtin_ghosttype}, diff --git a/src/nasal_builtin.h b/src/nasal_builtin.h index 2169a3a..7dd25bc 100644 --- a/src/nasal_builtin.h +++ b/src/nasal_builtin.h @@ -124,7 +124,6 @@ var builtin_coyield(var*, gc&); var builtin_costatus(var*, gc&); var builtin_corun(var*, gc&); var builtin_millisec(var*, gc&); -var builtin_sysargv(var*, gc&); var builtin_gcextend(var*, gc&); var builtin_logtime(var*, gc&); var builtin_ghosttype(var*, gc&); diff --git a/src/nasal_codegen.cpp b/src/nasal_codegen.cpp index f84c53e..683fb54 100644 --- a/src/nasal_codegen.cpp +++ b/src/nasal_codegen.cpp @@ -211,6 +211,16 @@ void codegen::func_gen(function* node) { // search symbols first, must use after loading parameters // or the location of symbols will change and cause fatal error find_symbol(block); + // add special varibale "arg", which is used to store overflowed args + // but if dynamic parameter is declared, this variable will be useless + // for example: + // var f = func(a) {print(arg)} + // f(1, 2, 3); + // then the arg is [2, 3], because 1 is accepted by "a" + // so in fact "f" is the same as: + // var f = func(a, arg...) {return(arg)} + add_symbol("arg"); + in_iterloop.push(0); block_gen(block); 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_symbol("globals"); - // add special symbol arg here, which is used to store function arguments - // for example: - // var f = func(a) {print(arg)} - // f(1, 2, 3); - // then the arg is [2, 3], because 1 is accepted by "a" - // so in fact "f" is the same as: - // var f = func(a, arg...) {return(arg)} + // add special symbol arg here, which is used to store command line args add_symbol("arg"); find_symbol(parse.tree()); // search symbols first diff --git a/src/nasal_lexer.cpp b/src/nasal_lexer.cpp index a85c252..1e3d541 100644 --- a/src/nasal_lexer.cpp +++ b/src/nasal_lexer.cpp @@ -7,43 +7,43 @@ #include "nasal_lexer.h" bool lexer::skip(char c) { - return c==' '||c=='\n'||c=='\t'||c=='\r'||c==0; + return c==' ' || c=='\n' || c=='\t' || c=='\r' || c==0; } bool lexer::is_id(char c) { - return (c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z')||(c<0); + return (c=='_') || ('a'<=c && c<='z') || ('A'<=c && c<='Z') || (c<0); } bool lexer::is_hex(char c) { - return ('0'<=c&&c<='9')||('a'<=c&&c<='f')||('A'<=c && c<='F'); + return ('0'<=c && c<='9') || ('a'<=c && c<='f') || ('A'<=c && c<='F'); } bool lexer::is_oct(char c) { - return '0'<=c&&c<='7'; + return '0'<=c && c<='7'; } bool lexer::is_dec(char c) { - return '0'<=c&&c<='9'; + return '0'<=c && c<='9'; } bool lexer::is_str(char c) { - return c=='\''||c=='\"'||c=='`'; + return c=='\'' || c=='\"' || c=='`'; } bool lexer::is_single_opr(char c) { return ( - c=='('||c==')'||c=='['||c==']'|| - c=='{'||c=='}'||c==','||c==';'|| - c==':'||c=='?'||c=='`'||c=='@'|| - c=='%'||c=='$'||c=='\\' + c=='(' || c==')' || c=='[' || c==']' || + c=='{' || c=='}' || c==',' || c==';' || + c==':' || c=='?' || c=='`' || c=='@' || + c=='%' || c=='$' || c=='\\' ); } bool lexer::is_calc_opr(char c) { return ( - c=='='||c=='+'||c=='-'||c=='*'|| - c=='!'||c=='/'||c=='<'||c=='>'|| - c=='~'||c=='|'||c=='&'||c=='^' + c=='=' || c=='+' || c=='-' || c=='*' || + c=='!' || c=='/' || c=='<' || c=='>' || + c=='~' || c=='|' || c=='&' || c=='^' ); } @@ -54,8 +54,10 @@ void lexer::skip_note() { void lexer::err_char() { ++column; - char c=res[ptr++]; - err.err("lexer", {line, column-1, line, column, filename}, "invalid character 0x"+chrhex(c)); + char c = res[ptr++]; + err.err("lexer", + {line, column-1, line, column, filename}, + "invalid character 0x"+chrhex(c)); err.fatal("lexer", "fatal error occurred, stop"); } @@ -81,220 +83,241 @@ void lexer::open(const std::string& file) { } tok lexer::get_type(const std::string& str) { - return typetbl.count(str)?typetbl.at(str):tok::null; + return typetbl.count(str)? typetbl.at(str):tok::null; } std::string lexer::utf8_gen() { - std::string str=""; + std::string str = ""; while(ptr"); + err.err("lexer", + {line, column-1, line, column, filename}, + "invalid utf-8 <"+utf_info+">"); err.fatal("lexer", "fatal error occurred, stop"); } - str+=tmp; - column+=2; // may have some problems because not all the unicode takes 2 space + str += tmp; + column += 2; // may have some problems because not all the unicode takes 2 space } return str; } token lexer::id_gen() { - u32 begin_line=line; - u32 begin_column=column; - std::string str=""; + u32 begin_line = line; + u32 begin_column = column; + std::string str = ""; while(ptr [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*) - std::string str=""; + std::string str = ""; while(ptr=res.size()) { - err.err("lexer", {begin_line, begin_column, line, column, filename}, "get EOF when generating string"); + err.err("lexer", + {begin_line, begin_column, line, column, filename}, + "get EOF when generating string"); return {{begin_line, begin_column, line, column, filename}, tok::str, str}; } ++column; - if (begin=='`' && str.length()!=1) { - err.err("lexer", {begin_line, begin_column, line, column, filename}, "\'`\' is used for string including one character"); + + // if is not utf8, 1+utf8_hdchk should be 1 + if (begin=='`' && str.length()!=1+utf8_hdchk(str[0])) { + err.err("lexer", + {begin_line, begin_column, line, column, filename}, + "\'`\' is used for string including one character"); } return {{begin_line, begin_column, line, column, filename}, tok::str, str}; } token lexer::single_opr() { - u32 begin_line=line; - u32 begin_column=column; - std::string str(1,res[ptr]); + u32 begin_line = line; + u32 begin_column = column; + std::string str(1, res[ptr]); ++column; - tok type=get_type(str); + tok type = get_type(str); if (type==tok::null) { - err.err("lexer", {begin_line, begin_column, line, column, filename}, "invalid operator `"+str+"`"); + err.err("lexer", + {begin_line, begin_column, line, column, filename}, + "invalid operator `"+str+"`"); } ++ptr; return {{begin_line, begin_column, line, column, filename}, type, str}; } token lexer::dots() { - u32 begin_line=line; - u32 begin_column=column; - std::string str="."; + u32 begin_line = line; + u32 begin_column = column; + std::string str = "."; if (ptr+2=res.size()) { @@ -328,6 +351,6 @@ const error& lexer::scan(const std::string& file) { } } toks.push_back({{line, column, line, column, filename}, tok::eof, ""}); - res=""; + res = ""; return err; } diff --git a/src/nasal_misc.cpp b/src/nasal_misc.cpp index 954ae53..4f01d0b 100644 --- a/src/nasal_misc.cpp +++ b/src/nasal_misc.cpp @@ -85,14 +85,14 @@ bool is_superh() { } f64 hex2f(const char* str) { - f64 ret=0; + f64 ret = 0; for(; *str; ++str) { if ('0'<=*str && *str<='9') { - ret=ret*16+(*str-'0'); + ret = ret*16+(*str-'0'); } else if ('a'<=*str && *str<='f') { - ret=ret*16+(*str-'a'+10); + ret = ret*16+(*str-'a'+10); } else if ('A'<=*str && *str<='F') { - ret=ret*16+(*str-'A'+10); + ret = ret*16+(*str-'A'+10); } else { return nan(""); } @@ -101,9 +101,9 @@ f64 hex2f(const char* str) { } f64 oct2f(const char* str) { - f64 ret=0; + f64 ret = 0; while('0'<=*str && *str<'8') { - ret=ret*8+(*str++-'0'); + ret = ret*8+(*str++-'0'); } if (*str) { return nan(""); @@ -118,9 +118,9 @@ f64 oct2f(const char* str) { // but this also makes 0.1+0.2==0.3, // not another result that you may get in other languages. f64 dec2f(const char* str) { - f64 ret=0,negative=1,num_pow=0; + f64 ret = 0, negative = 1, num_pow = 0; while('0'<=*str && *str<='9') { - ret=ret*10+(*str++-'0'); + ret = ret*10+(*str++-'0'); } if (!*str) { return ret; @@ -129,10 +129,10 @@ f64 dec2f(const char* str) { if (!*++str) { return nan(""); } - num_pow=0.1; + num_pow = 0.1; while('0'<=*str && *str<='9') { - ret+=num_pow*(*str++-'0'); - num_pow*=0.1; + ret += num_pow*(*str++-'0'); + num_pow *= 0.1; } if (!*str) { return ret; @@ -145,14 +145,14 @@ f64 dec2f(const char* str) { return nan(""); } if (*str=='-' || *str=='+') { - negative=(*str++=='-'? -1:1); + negative = (*str++=='-'? -1:1); } if (!*str) { return nan(""); } - num_pow=0; + num_pow = 0; while('0'<=*str && *str<='9') { - num_pow=num_pow*10+(*str++-'0'); + num_pow = num_pow*10+(*str++-'0'); } if (*str) { return nan(""); @@ -161,27 +161,27 @@ f64 dec2f(const char* str) { } f64 str2num(const char* str) { - bool negative=false; - f64 res=0; + bool negative = false; + f64 res = 0; if (*str=='-' || *str=='+') { - negative=(*str++=='-'); + negative = (*str++=='-'); } if (!*str) { return nan(""); } if (str[0]=='0' && str[1]=='x') { - res=hex2f(str+2); + res = hex2f(str+2); } else if (str[0]=='0' && str[1]=='o') { - res=oct2f(str+2); + res = oct2f(str+2); } else { - res=dec2f(str); + res = dec2f(str); } - return negative?-res:res; + return negative? -res:res; } i32 utf8_hdchk(const char head) { // RFC-2279 but now we use RFC-3629 so nbytes is less than 4 - const u8 c=(u8)head; + const u8 c = (u8)head; if ((c>>5)==0x06) { // 110x xxxx (10xx xxxx)^1 return 1; } @@ -195,36 +195,36 @@ i32 utf8_hdchk(const char head) { } std::string chrhex(const char c) { - const char hextbl[]="0123456789abcdef"; - return {hextbl[(c&0xf0)>>4],hextbl[c&0x0f]}; + const char hextbl[] = "0123456789abcdef"; + return {hextbl[(c&0xf0)>>4], hextbl[c&0x0f]}; } std::string rawstr(const std::string& str, const usize maxlen) { std::string ret(""); - for(auto i:str) { + for(auto i : str) { // windows doesn't output unicode normally, so we output the hex if (is_windows() && i<=0) { - ret+="\\x"+chrhex(i); + ret += "\\x"+chrhex(i); continue; } switch(i) { - case '\0': ret+="\\0"; break; - case '\a': ret+="\\a"; break; - case '\b': ret+="\\b"; break; - case '\t': ret+="\\t"; break; - case '\n': ret+="\\n"; break; - case '\v': ret+="\\v"; break; - case '\f': ret+="\\f"; break; - case '\r': ret+="\\r"; break; - case '\033':ret+="\\e"; break; - case '\"': ret+="\\\"";break; - case '\'': ret+="\\\'";break; - case '\\': ret+="\\\\";break; - default: ret+=i; break; + case '\0': ret += "\\0"; break; + case '\a': ret += "\\a"; break; + case '\b': ret += "\\b"; break; + case '\t': ret += "\\t"; break; + case '\n': ret += "\\n"; break; + case '\v': ret += "\\v"; break; + case '\f': ret += "\\f"; break; + case '\r': ret += "\\r"; break; + case '\033':ret += "\\e"; break; + case '\"': ret += "\\\""; break; + case '\'': ret += "\\\'"; break; + case '\\': ret += "\\\\"; break; + default: ret += i; break; } } if (maxlen && ret.length()>maxlen) { - ret=ret.substr(0,maxlen)+"..."; + ret = ret.substr(0,maxlen)+"..."; } return ret; } \ No newline at end of file diff --git a/src/nasal_vm.cpp b/src/nasal_vm.cpp index 27e12e1..11057ee 100644 --- a/src/nasal_vm.cpp +++ b/src/nasal_vm.cpp @@ -8,26 +8,28 @@ void vm::init( const std::vector& filenames, const std::vector& argv ) { - cnum=nums.data(); - cstr=strs.data(); - bytecode=code.data(); - files=filenames.data(); + cnum = nums.data(); + cstr = strs.data(); + bytecode = code.data(); + files = filenames.data(); /* set canary and program counter */ - ctx.pc=0; - ctx.localr=ctx.memr=nullptr; - ctx.funcr=ctx.upvalr=nil; - ctx.canary=stack+STACK_DEPTH-1; // stack[STACK_DEPTH-1] - ctx.top=stack; - ctx.stack=stack; + ctx.pc = 0; + ctx.localr = nullptr; + ctx.memr = nullptr; + ctx.funcr = nil; + ctx.upvalr = nil; + ctx.canary = stack+STACK_DEPTH-1; // stack[STACK_DEPTH-1] + ctx.top = stack; + ctx.stack = stack; /* clear main stack */ - for(u32 i=0;i "< "< entry:0x"<=ctx.canary) { die("stack overflow"); @@ -640,14 +643,14 @@ inline void vm::o_callfv() { var dynamic = nil; if (func.dpara>=0) { // load dynamic arguments dynamic = ngc.alloc(vm_vec); - for(u32 i=psize;i=1;--i) { // load arguments + for(u32 i = min_size; i>=1; --i) { // load arguments local[i] = local[i-1]; } local[0] = func.local[0];// load "me" + // load local scope & default arguments - for(u32 i=min_size+1;i=0) { - local[psize+1] = dynamic; - } + local[func.dpara>=0? psize+1:func.lsize-1] = dynamic; ctx.top[0] = ctx.upvalr; (++ctx.top)[0] = var::addr(ctx.localr); (++ctx.top)[0] = var::ret(ctx.pc); - ctx.pc=func.entry-1; + ctx.pc = func.entry-1; ctx.localr = local; ctx.upvalr = nil; } @@ -912,9 +914,6 @@ inline void vm::o_ret() { ctx.funcr = ctx.top[0]; ctx.top[0] = ret; // rewrite func with returned value - // reset "arg" - stack[1] = nil; - if (up.type==vm_upval) { // synchronize upvalue auto& upval = up.upval(); auto size = func.func().lsize; diff --git a/std/lib.nas b/std/lib.nas index f137fa3..e8680c3 100644 --- a/std/lib.nas +++ b/std/lib.nas @@ -478,7 +478,7 @@ var os = { # runtime gives us some functions that we could manage it manually. var runtime = { # command line arguments - argv: func() {return __sysargv;}, + argv: func() {return globals.arg;}, gc: { extend: func(type) {return __gcextd;} } diff --git a/test/globals_test.nas b/test/globals_test.nas index bb55d7f..c698b33 100644 --- a/test/globals_test.nas +++ b/test/globals_test.nas @@ -16,9 +16,12 @@ globals.test_func(); var f = func() { println(arg); + func() {println(arg);}(114, 514, 1919, 810); + println(arg); } f(1, 2, 3); +# command line arguments println(arg); println(globals.arg); \ No newline at end of file diff --git a/test/sample.nas b/test/sample.nas index 8fb0c42..5865f17 100644 --- a/test/sample.nas +++ b/test/sample.nas @@ -1,16 +1,16 @@ # This file is written by Andy Ross, and is protected by GPLv2.0 # A no-op function used below to get this file to run. Ignore and read on... -dummyFunc = func { 1 } +var dummyFunc = func { 1 } # # Literal numbers can be decimal, exponential, or hex constants. All # numbers are stored internally as IEEE double-precision values. # -n1 = 3; -n2 = 3.14; -n3 = 6.023e23; -n3 = 0x123456; +var n1 = 3; +var n2 = 3.14; +var n3 = 6.023e23; +var n3 = 0x123456; # # Two identical string literals with different quotes. Double quotes @@ -19,14 +19,14 @@ n3 = 0x123456; # whitespace like newlines). Double quotes handle the following # C-like escapes: \n \r \t \xnn \" # -s1 = 'Andy\'s "computer" has a C:\righteous\newstuff directory.'; -s2 = "Andy's \"computer\" has a C:\\righteous\\newstuff directory."; +var s1 = 'Andy\'s "computer" has a C:\righteous\newstuff directory.'; +var s2 = "Andy's \"computer\" has a C:\\righteous\\newstuff directory."; # # Literal lists use square brackets with a comma-separated expression # list. # -list1 = ["a", "b", 1, 2]; +var list1 = ["a", "b", 1, 2]; # # Literal hashes (or objects -- same thing) use curlies and colons to @@ -35,8 +35,8 @@ list1 = ["a", "b", 1, 2]; # to use symbols, lookup tables of other types will be more # comfortable with literals. # -hash1 = { name : "Andy", job : "Hacker" }; -EnglishEspanol = { "one" : "uno", "two": "dos", "blue" : "azul" }; +var hash1 = { name : "Andy", job : "Hacker" }; +var EnglishEspanol = { "one" : "uno", "two": "dos", "blue" : "azul" }; # # Both vectors and hashes use square brackets for the lookup operation: @@ -50,7 +50,7 @@ hash1["name"] == "Andy"; # (anonymous) function argument to the local "log_message" variable. # There is no function declaration syntax in Nasal. # -log_message = func { +var log_message = func { print(arg[0]); } @@ -58,10 +58,10 @@ log_message = func { # You can also pass named arguments to a function, thus saving the # typing and performance costs of extracting them from the arg array. # -sqrt = dummyFunc; -dist = func(x1, y1, x2, y2) { - dx = x2-x1; - dy = y2-y1; +var sqrt = dummyFunc; +var dist = func(x1, y1, x2, y2) { + var dx = x2-x1; + var dy = y2-y1; return sqrt(dx*dx + dy*dy); } dist(0,0,1,1); # == sqrt(2) @@ -71,14 +71,14 @@ dist(0,0,1,1); # == sqrt(2) # default value must be a scalar (number, string, function, nil) and # not a mutable composite object (list, hash). # -read = func(bytes, flags=0) { } +var read = func(bytes, flags=0) { } # # Any extra arguments after the named list are placed in the "arg" # vector as above. You can rename this to something other than "arg" # by specifying a final argument name with an ellipsis: # -listify = func(elements...) { return elements; } +var listify = func(elements...) { return elements; } listify(1, 2, 3, 4); # returns a list: [1, 2, 3, 4] # @@ -87,7 +87,7 @@ listify(1, 2, 3, 4); # returns a list: [1, 2, 3, 4] # good practice in general, although it is not required. Note that # this is not a "declaration", just a qualifier on the "=" operator. # -innerFunc = func { +var innerFunc = func { for(var dist=0; dist<100; dist += 1) { # Does not interfere with the "dist" symbol defined above } @@ -99,26 +99,26 @@ innerFunc = func { # what the ?: does in C. The last semicolon in a code block is # optional, to make this prettier. # -abs = func(n) { if(n<0) { -n } else { n } } +var abs = func(n) { if(n<0) { -n } else { n } } # # But for those who don't like typing, the ternary operator works like # you expect: # -abs = func(n) { n < 0 ? -n : n } +var abs = func(n) { n < 0 ? -n : n } # # Nasal supports a "nil" value for use as a null pointer equivalent. # It can be tested for equality, matching only other nils. # -listNode = { data : ["what", "ever"], next : nil }; +var listNode = { data : ["what", "ever"], next : nil }; # # Nasal's binary boolean operators are "and" and "or", unlike C. # unary not is still "!" however. They short-circuit like you expect # -toggle = 0; -a = nil; +var toggle = 0; +var a = nil; if(a and a.field == 42) { toggle = !toggle; # doesn't crash when a is nil } @@ -129,24 +129,24 @@ if(a and a.field == 42) { # takes a local variable name as its first argument and a vector as # its second. # -doSomething = dummyFunc; +var doSomething = dummyFunc; -stillGoing = 0; +var stillGoing = 0; while(stillGoing) { doSomething(); } -for(i=0; i < 3; i = i+1) { +for(var i=0; i < 3; i = i+1) { elem = list1[i]; doSomething(elem); } -foreach(elem; list1) { doSomething(elem) } # Shorthand for above +foreach(var elem; list1) { doSomething(elem) } # Shorthand for above # # There is also a "forindex", which is like foreach except that it # assigns the index of each element, instead of the value, to the loop # variable. # -forindex(i; list1) { doSomething(list1[i]); } +forindex(var i; list1) { doSomething(list1[i]); } # # Define a class object with one method, one field and one "new" @@ -155,10 +155,10 @@ forindex(i; list1) { doSomething(list1[i]); } # appropriately. Member functions can get their local object (the # equivalent of the "this" pointer in C++) as the "me" variable. # -Class1 = {}; +var Class1 = {}; Class1.new = func { - obj = { parents : [Class1], + var obj = { parents : [Class1], count : 0 }; return obj; } @@ -168,7 +168,7 @@ Class1.getcount = func { return me.count; } -c = Class1.new(); +var c = Class1.new(); print(c.getcount(), "\n"); # prints 1 print(c.getcount(), "\n"); # prints 2 print(c.getcount(), "\n"); # prints 3 @@ -177,20 +177,20 @@ print(c.getcount(), "\n"); # prints 3 # But *set* operations always go to the local object. You can't # corrupt a parent class via OOP operations on its instances (but you # *can* get to it via hand-inspection of the parents arrays). -c2 = Class1.new(); -c2.getcount() = func { 12345 }; # custom "derived" function! +var c2 = Class1.new(); +c2.getcount = func { return 12345 }; # custom "derived" function! print(c2.getcount(), "\n"); # prints 12345 -print(c1.getcount(), "\n"); # prints 4, Class1.getcount is unchanged +print(c.getcount(), "\n"); # prints 4, Class1.getcount is unchanged # # This creates an identical class using alternative syntax. # -Class2 = { +var Class2 = { new : func { - obj = {}; - obj.parents = [Class2]; - obj.count = 0; + var obj = {}; + obj.parents = [Class2]; + obj.count = 0; return obj; }, getcount : func { @@ -206,7 +206,7 @@ Class2 = { # C (although note that there is no nul termination -- get the length # with size()): # -string = "abcdefghijklmnopqrstuvwxyz"; +var string = "abcdefghijklmnopqrstuvwxyz"; var ascii_sum = 0; for(var i=0; i= `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 # indicates the last element in the vector (or string). # -next_to_last = list1[-2]; +var next_to_last = list1[-2]; # # Remember that strings look syntactically like vectors of bytes; so # conversely, the "~" concatenation operator works equally well to # concatenate vectors: # -joined_list = [1, 2, 3] ~ [4, 5, 6]; +var joined_list = [1, 2, 3] ~ [4, 5, 6]; ### ### 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 # for each element. # -invert = func(vec) { - hash = {}; - for(i=0; i 0) { - k = ks[0]; + var k = ks[0]; result = result ~ k ~ ":" ~ dump(o[k]); } for(i=1; i