✨ add special variable "arg"
This commit is contained in:
parent
fd614a132c
commit
3509655424
12
README.md
12
README.md
|
@ -71,7 +71,9 @@ __CAUTION__: If want to use the release zip/tar.gz file to build the interpreter
|
|||
|
||||
Make sure your MinGW thread model is `posix thread model`, otherwise it may not have the thread library.
|
||||
|
||||
> mingw32-make nasal.exe
|
||||
> mkdir build
|
||||
>
|
||||
> mingw32-make nasal.exe -j4
|
||||
|
||||
### __`Windows (Visual Studio)`__
|
||||
|
||||
|
@ -79,16 +81,14 @@ This project gives a [__CMakelists.txt__](./CMakeLists.txt) for you to create pr
|
|||
|
||||
### __`Linux/macOS/Unix`__
|
||||
|
||||
> make nasal
|
||||
> mkdir build
|
||||
>
|
||||
> make -j4
|
||||
|
||||
You could choose which compiler you want to use:
|
||||
|
||||
> make nasal CXX=...
|
||||
|
||||
If you think `-O3` isn't that safe and stable, you could choose:
|
||||
|
||||
> make stable-release
|
||||
|
||||
## __How to Use__
|
||||
|
||||

|
||||
|
|
|
@ -62,7 +62,9 @@ __注意__: 如果你想直接下载发行版提供的zip/tar.gz压缩包来构
|
|||
|
||||
一定要确保您的 MinGW thread model 是 `posix thread model`, 否则可能存在没有 thread 库的问题。
|
||||
|
||||
> mingw32-make nasal.exe
|
||||
> mkdir build
|
||||
>
|
||||
> mingw32-make nasal.exe -j4
|
||||
|
||||
### __`Windows` 平台(`Vistual Studio`)__
|
||||
|
||||
|
@ -70,16 +72,14 @@ __注意__: 如果你想直接下载发行版提供的zip/tar.gz压缩包来构
|
|||
|
||||
### __`Linux/macOS/Unix` 平台__
|
||||
|
||||
> make nasal
|
||||
> mkdir build
|
||||
>
|
||||
> make -j4
|
||||
|
||||
你也可以通过如下的其中一行命令来指定你想要使用的编译器:
|
||||
|
||||
> make nasal CXX=...
|
||||
|
||||
如果你觉得`-O3`编译的版本不是那么安全和稳定,你也可以选择生成稳定的版本:
|
||||
|
||||
> make stable-release
|
||||
|
||||
## __使用方法__
|
||||
|
||||

|
||||
|
|
109
makefile
109
makefile
|
@ -1,81 +1,84 @@
|
|||
STD=c++17
|
||||
|
||||
NASAL_OBJECT=\
|
||||
nasal_err.o\
|
||||
nasal_ast.o\
|
||||
ast_visitor.o\
|
||||
ast_dumper.o\
|
||||
nasal_lexer.o\
|
||||
nasal_parse.o\
|
||||
nasal_import.o\
|
||||
optimizer.o\
|
||||
nasal_opcode.o\
|
||||
symbol_finder.o\
|
||||
nasal_codegen.o\
|
||||
nasal_misc.o\
|
||||
nasal_gc.o\
|
||||
nasal_builtin.o\
|
||||
nasal_vm.o\
|
||||
nasal_dbg.o\
|
||||
main.o
|
||||
build/nasal_err.o\
|
||||
build/nasal_ast.o\
|
||||
build/ast_visitor.o\
|
||||
build/ast_dumper.o\
|
||||
build/nasal_lexer.o\
|
||||
build/nasal_parse.o\
|
||||
build/nasal_import.o\
|
||||
build/optimizer.o\
|
||||
build/nasal_opcode.o\
|
||||
build/symbol_finder.o\
|
||||
build/nasal_codegen.o\
|
||||
build/nasal_misc.o\
|
||||
build/nasal_gc.o\
|
||||
build/nasal_builtin.o\
|
||||
build/nasal_vm.o\
|
||||
build/nasal_dbg.o\
|
||||
build/main.o
|
||||
|
||||
# for test
|
||||
nasal: $(NASAL_OBJECT)
|
||||
nasal: $(NASAL_OBJECT) | build
|
||||
$(CXX) $(NASAL_OBJECT) -O3 -o nasal -ldl
|
||||
|
||||
nasal.exe: $(NASAL_OBJECT)
|
||||
nasal.exe: $(NASAL_OBJECT) | build
|
||||
$(CXX) $(NASAL_OBJECT) -O3 -o nasal.exe
|
||||
|
||||
main.o: src/main.cpp
|
||||
$(CXX) -std=$(STD) -c -O3 src/main.cpp -fno-exceptions -fPIC -o main.o -I .
|
||||
build:
|
||||
@ if [ ! -d build ]; then mkdir build; fi
|
||||
|
||||
nasal_misc.o: src/nasal.h src/nasal_misc.cpp
|
||||
$(CXX) -std=$(STD) -c -O3 src/nasal_misc.cpp -fno-exceptions -fPIC -o nasal_misc.o -I .
|
||||
build/main.o: src/nasal.h src/nasal_err.h src/nasal_lexer.h src/main.cpp | build
|
||||
$(CXX) -std=$(STD) -c -O3 src/main.cpp -fno-exceptions -fPIC -o build/main.o -I .
|
||||
|
||||
nasal_err.o: src/nasal_err.h src/nasal_err.cpp
|
||||
$(CXX) -std=$(STD) -c -O3 src/nasal_err.cpp -fno-exceptions -fPIC -o nasal_err.o -I .
|
||||
build/nasal_misc.o: src/nasal.h src/nasal_misc.cpp | build
|
||||
$(CXX) -std=$(STD) -c -O3 src/nasal_misc.cpp -fno-exceptions -fPIC -o build/nasal_misc.o -I .
|
||||
|
||||
nasal_gc.o: src/nasal_gc.h src/nasal_gc.cpp
|
||||
$(CXX) -std=$(STD) -c -O3 src/nasal_gc.cpp -fno-exceptions -fPIC -o nasal_gc.o -I .
|
||||
build/nasal_err.o: 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 .
|
||||
|
||||
nasal_import.o: src/nasal_import.h src/nasal_import.cpp
|
||||
$(CXX) -std=$(STD) -c -O3 src/nasal_import.cpp -fno-exceptions -fPIC -o nasal_import.o -I .
|
||||
build/nasal_gc.o: 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 .
|
||||
|
||||
nasal_lexer.o: src/nasal_lexer.h src/nasal_lexer.cpp
|
||||
$(CXX) -std=$(STD) -c -O3 src/nasal_lexer.cpp -fno-exceptions -fPIC -o nasal_lexer.o -I .
|
||||
build/nasal_import.o: 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 .
|
||||
|
||||
nasal_ast.o: src/nasal_ast.h src/nasal_ast.cpp
|
||||
$(CXX) -std=$(STD) -c -O3 src/nasal_ast.cpp -fno-exceptions -fPIC -o nasal_ast.o -I .
|
||||
build/nasal_lexer.o: 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 .
|
||||
|
||||
nasal_builtin.o: src/nasal_builtin.h src/nasal_builtin.cpp
|
||||
$(CXX) -std=$(STD) -c -O3 src/nasal_builtin.cpp -fno-exceptions -fPIC -o nasal_builtin.o -I .
|
||||
build/nasal_ast.o: 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 .
|
||||
|
||||
nasal_codegen.o: src/nasal_codegen.h src/nasal_codegen.cpp
|
||||
$(CXX) -std=$(STD) -c -O3 src/nasal_codegen.cpp -fno-exceptions -fPIC -o nasal_codegen.o -I .
|
||||
build/nasal_builtin.o: 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 .
|
||||
|
||||
nasal_opcode.o: src/nasal_opcode.h src/nasal_opcode.cpp
|
||||
$(CXX) -std=$(STD) -c -O3 src/nasal_opcode.cpp -fno-exceptions -fPIC -o nasal_opcode.o -I .
|
||||
build/nasal_codegen.o: 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 .
|
||||
|
||||
nasal_parse.o: src/nasal_parse.h src/nasal_parse.cpp src/nasal_ast.h
|
||||
$(CXX) -std=$(STD) -c -O3 src/nasal_parse.cpp -fno-exceptions -fPIC -o nasal_parse.o -I .
|
||||
build/nasal_opcode.o: 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 .
|
||||
|
||||
optimizer.o: src/optimizer.h src/optimizer.cpp src/nasal_ast.h
|
||||
$(CXX) -std=$(STD) -c -O3 src/optimizer.cpp -fno-exceptions -fPIC -o optimizer.o -I .
|
||||
build/nasal_parse.o: 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 .
|
||||
|
||||
symbol_finder.o: src/symbol_finder.h src/symbol_finder.cpp src/nasal_ast.h
|
||||
$(CXX) -std=$(STD) -c -O3 src/symbol_finder.cpp -fno-exceptions -fPIC -o symbol_finder.o -I .
|
||||
build/optimizer.o: 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 .
|
||||
|
||||
ast_visitor.o: src/nasal_ast.h src/ast_visitor.h src/ast_visitor.cpp
|
||||
$(CXX) -std=$(STD) -c -O3 src/ast_visitor.cpp -fno-exceptions -fPIC -o ast_visitor.o -I .
|
||||
build/symbol_finder.o: 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 .
|
||||
|
||||
ast_dumper.o: src/nasal_ast.h src/ast_visitor.h src/ast_dumper.h src/ast_dumper.cpp
|
||||
$(CXX) -std=$(STD) -c -O3 src/ast_dumper.cpp -fno-exceptions -fPIC -o ast_dumper.o -I .
|
||||
build/ast_visitor.o: 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 .
|
||||
|
||||
nasal_vm.o: src/nasal_vm.h src/nasal_vm.cpp
|
||||
$(CXX) -std=$(STD) -c -O3 src/nasal_vm.cpp -fno-exceptions -fPIC -o nasal_vm.o -I .
|
||||
build/ast_dumper.o: src/nasal_ast.h src/ast_visitor.h src/ast_dumper.h src/ast_dumper.cpp
|
||||
$(CXX) -std=$(STD) -c -O3 src/ast_dumper.cpp -fno-exceptions -fPIC -o build/ast_dumper.o -I .
|
||||
|
||||
nasal_dbg.o: src/nasal_dbg.h src/nasal_dbg.cpp
|
||||
$(CXX) -std=$(STD) -c -O3 src/nasal_dbg.cpp -fno-exceptions -fPIC -o nasal_dbg.o -I .
|
||||
build/nasal_vm.o: src/nasal_vm.h src/nasal_vm.cpp | build
|
||||
$(CXX) -std=$(STD) -c -O3 src/nasal_vm.cpp -fno-exceptions -fPIC -o build/nasal_vm.o -I .
|
||||
|
||||
build/nasal_dbg.o: src/nasal_dbg.h src/nasal_dbg.cpp | build
|
||||
$(CXX) -std=$(STD) -c -O3 src/nasal_dbg.cpp -fno-exceptions -fPIC -o build/nasal_dbg.o -I .
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
|
|
|
@ -4,7 +4,7 @@ dynamic_libs_so = libfib.so libkey.so libnasock.so libmat.so
|
|||
dynamic_libs_dll = libfib.dll libkey.dll libnasock.dll libmat.dll
|
||||
|
||||
used_header = ../src/nasal.h ../src/nasal_gc.h
|
||||
used_object = ../nasal_misc.o ../nasal_gc.o
|
||||
used_object = ../build/nasal_misc.o ../build/nasal_gc.o
|
||||
|
||||
STD=c++17
|
||||
|
||||
|
|
|
@ -135,7 +135,7 @@ void execute(
|
|||
}
|
||||
|
||||
// run
|
||||
auto start=clk::now();
|
||||
auto start = clk::now();
|
||||
if (cmd&VM_DEBUG) {
|
||||
dbg(err).run(gen, ld, argv);
|
||||
} else if (cmd&VM_TIME || cmd&VM_EXEC) {
|
||||
|
@ -144,7 +144,7 @@ void execute(
|
|||
|
||||
// get running time
|
||||
if (cmd&VM_TIME) {
|
||||
f64 tm=(clk::now()-start).count()*1.0/den;
|
||||
f64 tm = (clk::now()-start).count()*1.0/den;
|
||||
std::clog << "process exited after " << tm << "s.\n\n";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include <unordered_map>
|
||||
|
||||
enum class expr_type:u32 {
|
||||
ast_null=0, // null node
|
||||
ast_null = 0, // null node
|
||||
ast_file_info, // stores file info
|
||||
ast_block, // code block
|
||||
ast_nil, // nil keyword
|
||||
|
@ -25,9 +25,9 @@ enum class expr_type:u32 {
|
|||
ast_callf, // id()
|
||||
ast_subvec, // id[index:index]
|
||||
ast_param, // function parameter
|
||||
ast_ternary,
|
||||
ast_binary,
|
||||
ast_unary,
|
||||
ast_ternary, // ternary operator
|
||||
ast_binary, // binary operator
|
||||
ast_unary, // unary operator
|
||||
ast_for, // for keyword
|
||||
ast_forei, // foreach or forindex loop
|
||||
ast_while, // while
|
||||
|
@ -35,10 +35,10 @@ enum class expr_type:u32 {
|
|||
ast_cond, // mark a sub-tree of conditional expression
|
||||
ast_if, // if keyword
|
||||
ast_multi_id, // multi identifiers sub-tree
|
||||
ast_tuple,
|
||||
ast_tuple, // tuple, stores multiple scalars
|
||||
ast_def, // definition
|
||||
ast_assign,
|
||||
ast_multi_assign,
|
||||
ast_assign, // assignment
|
||||
ast_multi_assign,// multiple assignment
|
||||
ast_continue, // continue keyword, only used in loop
|
||||
ast_break, // break keyword, only used in loop
|
||||
ast_ret // return keyword, only used in function block
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1091,7 +1091,18 @@ const error& codegen::compile(parse& parse, linker& import) {
|
|||
fileindex = 0;
|
||||
file = import.filelist().data();
|
||||
in_iterloop.push(0);
|
||||
|
||||
// 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_symbol("arg");
|
||||
|
||||
find_symbol(parse.tree()); // search symbols first
|
||||
gen(op_intg, global.size(), 0);
|
||||
block_gen(parse.tree()); // generate main block
|
||||
|
|
|
@ -643,6 +643,12 @@ inline void vm::o_callfv() {
|
|||
for(u32 i=psize;i<argc;++i) {
|
||||
dynamic.vec().elems.push_back(local[i]);
|
||||
}
|
||||
} else if (psize<argc) {
|
||||
// load arguments to "arg", located at stack+1
|
||||
stack[1] = ngc.alloc(vm_vec);
|
||||
for(u32 i=psize;i<argc;++i) {
|
||||
stack[1].vec().elems.push_back(local[i]);
|
||||
}
|
||||
}
|
||||
// should reset stack top after allocating vector
|
||||
// because this may cause gc
|
||||
|
@ -906,6 +912,9 @@ 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;
|
||||
|
|
|
@ -13,3 +13,12 @@ globals.test_func = func() {
|
|||
println();
|
||||
println(globals.test_func);
|
||||
globals.test_func();
|
||||
|
||||
var f = func() {
|
||||
println(arg);
|
||||
}
|
||||
|
||||
f(1, 2, 3);
|
||||
|
||||
println(arg);
|
||||
println(globals.arg);
|
|
@ -0,0 +1,378 @@
|
|||
# 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 }
|
||||
|
||||
#
|
||||
# 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;
|
||||
|
||||
#
|
||||
# Two identical string literals with different quotes. Double quotes
|
||||
# use typical escape sequences. Single quotes treat everything as
|
||||
# literal except for embedded single quotes (including embedded
|
||||
# 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.";
|
||||
|
||||
#
|
||||
# Literal lists use square brackets with a comma-separated expression
|
||||
# list.
|
||||
#
|
||||
list1 = ["a", "b", 1, 2];
|
||||
|
||||
#
|
||||
# Literal hashes (or objects -- same thing) use curlies and colons to
|
||||
# separate the key/value pairs. Note that the key can be either a
|
||||
# symbol or a literal scalar. Object definitions will typically want
|
||||
# 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" };
|
||||
|
||||
#
|
||||
# Both vectors and hashes use square brackets for the lookup operation:
|
||||
#
|
||||
list1[0] == "a";
|
||||
hash1["name"] == "Andy";
|
||||
|
||||
#
|
||||
# A simple function. By default, arguments are passed in the "arg"
|
||||
# array, not unlike perl. Note that this is just an assignment of an
|
||||
# (anonymous) function argument to the local "log_message" variable.
|
||||
# There is no function declaration syntax in Nasal.
|
||||
#
|
||||
log_message = func {
|
||||
print(arg[0]);
|
||||
}
|
||||
|
||||
#
|
||||
# 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;
|
||||
return sqrt(dx*dx + dy*dy);
|
||||
}
|
||||
dist(0,0,1,1); # == sqrt(2)
|
||||
|
||||
#
|
||||
# These arguments can have default values, as in C++. Note that the
|
||||
# default value must be a scalar (number, string, function, nil) and
|
||||
# not a mutable composite object (list, hash).
|
||||
#
|
||||
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; }
|
||||
listify(1, 2, 3, 4); # returns a list: [1, 2, 3, 4]
|
||||
|
||||
#
|
||||
# You can use a "var" qualifier to make sure that an assigment is made
|
||||
# to a local variable, and not one from the enclosing scope. This is
|
||||
# good practice in general, although it is not required. Note that
|
||||
# this is not a "declaration", just a qualifier on the "=" operator.
|
||||
#
|
||||
innerFunc = func {
|
||||
for(var dist=0; dist<100; dist += 1) {
|
||||
# Does not interfere with the "dist" symbol defined above
|
||||
}
|
||||
}
|
||||
|
||||
#
|
||||
# Nasal has no "statements", which means that any expression can appear
|
||||
# in any context. This means that you can use an if/else clause to do
|
||||
# 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 } }
|
||||
|
||||
#
|
||||
# But for those who don't like typing, the ternary operator works like
|
||||
# you expect:
|
||||
#
|
||||
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 };
|
||||
|
||||
#
|
||||
# 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;
|
||||
if(a and a.field == 42) {
|
||||
toggle = !toggle; # doesn't crash when a is nil
|
||||
}
|
||||
|
||||
#
|
||||
# Looping constructs are mostly C-like. The differences are that
|
||||
# there is no do{}while(); construct, and there is a foreach, which
|
||||
# takes a local variable name as its first argument and a vector as
|
||||
# its second.
|
||||
#
|
||||
doSomething = dummyFunc;
|
||||
|
||||
stillGoing = 0;
|
||||
while(stillGoing) { doSomething(); }
|
||||
|
||||
for(i=0; i < 3; i = i+1) {
|
||||
elem = list1[i];
|
||||
doSomething(elem);
|
||||
}
|
||||
|
||||
foreach(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]); }
|
||||
|
||||
#
|
||||
# Define a class object with one method, one field and one "new"
|
||||
# contstructor. The "new" function is not special in any way; it
|
||||
# simply returns an initialized object with the "parents" field set
|
||||
# appropriately. Member functions can get their local object (the
|
||||
# equivalent of the "this" pointer in C++) as the "me" variable.
|
||||
#
|
||||
Class1 = {};
|
||||
|
||||
Class1.new = func {
|
||||
obj = { parents : [Class1],
|
||||
count : 0 };
|
||||
return obj;
|
||||
}
|
||||
|
||||
Class1.getcount = func {
|
||||
me.count = me.count + 1;
|
||||
return me.count;
|
||||
}
|
||||
|
||||
c = Class1.new();
|
||||
print(c.getcount(), "\n"); # prints 1
|
||||
print(c.getcount(), "\n"); # prints 2
|
||||
print(c.getcount(), "\n"); # prints 3
|
||||
|
||||
# Note that member *get* operations recurse into the parents array.
|
||||
# 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!
|
||||
|
||||
print(c2.getcount(), "\n"); # prints 12345
|
||||
print(c1.getcount(), "\n"); # prints 4, Class1.getcount is unchanged
|
||||
|
||||
#
|
||||
# This creates an identical class using alternative syntax.
|
||||
#
|
||||
Class2 = {
|
||||
new : func {
|
||||
obj = {};
|
||||
obj.parents = [Class2];
|
||||
obj.count = 0;
|
||||
return obj;
|
||||
},
|
||||
getcount : func {
|
||||
me.count = me.count + 1;
|
||||
return me.count;
|
||||
}
|
||||
};
|
||||
|
||||
#
|
||||
# Advanced strings: Nasal strings are always arrays of bytes (never
|
||||
# characters: see the utf8 library if you want character-based
|
||||
# equivalents of substr() et. al.). They can be indexed just like in
|
||||
# C (although note that there is no nul termination -- get the length
|
||||
# with size()):
|
||||
#
|
||||
string = "abcdefghijklmnopqrstuvwxyz";
|
||||
var ascii_sum = 0;
|
||||
for(var i=0; i<size(string); i+=1) { ascii_sum += string[i]; }
|
||||
|
||||
#
|
||||
# You can use backquotes to write UTF8 character constants
|
||||
#
|
||||
if(`A` != 65) { print("ASCII violation bug!\n"); }
|
||||
if(`©` != 169) { print("Unicode violation bug!\n"); }
|
||||
|
||||
#
|
||||
# And you can mutate strings by assigning to their indices, as long as
|
||||
# they are not constants. You
|
||||
# can make a mutable string either with the append operator or the
|
||||
# bits.buf() function.
|
||||
#
|
||||
ascii_lc = func(string) {
|
||||
var mutable = string ~ "";
|
||||
for(var i=0; i<size(mutable); i+=1) {
|
||||
if(mutable[i] >= `A` and mutable[i] <= `Z`) {
|
||||
mutable[i] += (`a` - `A`);
|
||||
}
|
||||
}
|
||||
return mutable;
|
||||
}
|
||||
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];
|
||||
|
||||
#
|
||||
# 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];
|
||||
|
||||
###
|
||||
### Now some fun examples:
|
||||
###
|
||||
|
||||
#
|
||||
# Make a "inverted index" hash out of a vector that returns the index
|
||||
# for each element.
|
||||
#
|
||||
invert = func(vec) {
|
||||
hash = {};
|
||||
for(i=0; i<size(vec); i = i+1) {
|
||||
hash[vec[i]] = i;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
#
|
||||
# Use the return value of the above function to do an "index of"
|
||||
# lookup on a vector
|
||||
#
|
||||
vecfind = func(vec, elem) { return invert(vec)[elem]; }
|
||||
|
||||
#
|
||||
# Joins its arguments with the empty string and returns a scalar.
|
||||
# Note use of "~" operator to do string concatenation (Nasal's only
|
||||
# funny syntax).
|
||||
#
|
||||
join = func {
|
||||
s = "";
|
||||
foreach(elem; arg) { s = s ~ elem; }
|
||||
return s;
|
||||
}
|
||||
|
||||
#
|
||||
# Labeled break/continue syntax puts the label in as an extra first
|
||||
# argument to the for/while/foreach.
|
||||
#
|
||||
doneWithInnerLoopEarly = dummyFunc;
|
||||
completelyDone = dummyFunc;
|
||||
for(OUTER; i=0; i<100; i = i+1) {
|
||||
for(j=0; j<100; j = j+1) {
|
||||
if(doneWithInnerLoopEarly()) {
|
||||
break;
|
||||
} elsif(completelyDone()) {
|
||||
break OUTER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
##
|
||||
## A rockin' metaprogramming hack. Generates and returns a deep copy
|
||||
## of the object in valid Nasal syntax. A warning to those who might
|
||||
## want to use this: it ignores function objects (which cannot be
|
||||
## inspected from Nasal) and replaces them with nil references. It
|
||||
## also makes no attempt to escape special characters in strings, which
|
||||
## can break re-parsing in strange (and possibly insecure!) ways.
|
||||
##
|
||||
dump = func(o) {
|
||||
result = "";
|
||||
if(typeof(o) == "scalar") {
|
||||
n = num(o);
|
||||
if(n == nil) { result = result ~ '"' ~ o ~ '"'; }
|
||||
else { result = result ~ o; }
|
||||
} elsif(typeof(o) == "vector") {
|
||||
result = result ~ "[ ";
|
||||
if(size(o) > 0) { result = result ~ dump(o[0]); }
|
||||
for(i=1; i<size(o); i=i+1) {
|
||||
result = result ~ ", " ~ dump(o[i]);
|
||||
}
|
||||
result = result ~ " ]";
|
||||
} elsif(typeof(o) == "hash") {
|
||||
ks = keys(o);
|
||||
result = result ~ "{ ";
|
||||
if(size(o) > 0) {
|
||||
k = ks[0];
|
||||
result = result ~ k ~ ":" ~ dump(o[k]);
|
||||
}
|
||||
for(i=1; i<size(o); i=i+1) {
|
||||
k = ks[i];
|
||||
result = result ~ ", " ~ k ~ " : " ~ dump(o[k]);
|
||||
}
|
||||
result = result ~ " }";
|
||||
} else {
|
||||
result = result ~ "nil";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#
|
||||
# Functional programming A: All function expressions are inherently
|
||||
# anonymous lambdas and can be used and evaluated mid-expression:
|
||||
#
|
||||
# (Note the parens around the function expression. This is necessary
|
||||
# because otherwise the parser would read a func following an
|
||||
# assignment as a "code block" instead of a subexpression. This is
|
||||
# the rule that allows you to omit the semicolon at the end of a
|
||||
# normal function definition. Oh well, every language has a syntactic
|
||||
# quirk or two...)
|
||||
#
|
||||
a = (func(n){ n + 1 })(232); # "a" now equals 233
|
||||
|
||||
#
|
||||
# Functional programming B. All expressions have a value, the last
|
||||
# expression in a code block is the return value of that code block.
|
||||
# There are no "statements" in Nasal, although some expressions
|
||||
# (assignment, duh) have side effects. e.g. The "if" expression works
|
||||
# both for code flow and as the ?: expression in C/C++.
|
||||
#
|
||||
factorial = func(n) { if(n == 0) { 1 }
|
||||
else { n * factorial(n-1) } }
|
||||
print(factorial(10), "\n");
|
||||
|
||||
#
|
||||
# Functional programming C: Lexical closures. Functions "remember"
|
||||
# the context in which they were defined, and can continue to use the
|
||||
# local variables in the outer scope even after their creator has
|
||||
# returned.
|
||||
#
|
||||
getcounter = func { count = 0; return func { count = count + 1 } }
|
||||
mycounter = getcounter();
|
||||
print(mycounter(), "\n"); # prints 1
|
||||
print(mycounter(), "\n"); # prints 2
|
||||
print(mycounter(), "\n"); # prints 3
|
||||
print(mycounter(), "\n"); # prints 4...
|
||||
|
||||
#
|
||||
# Functional programming D: see the documentation on the functions
|
||||
# bind(), call(), closure() and caller(). Basically, every piece of
|
||||
# the symbol lookup environment is inspectable and mutable at runtime.
|
||||
#
|
Loading…
Reference in New Issue