✨ 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.
|
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)`__
|
### __`Windows (Visual Studio)`__
|
||||||
|
|
||||||
|
@ -79,16 +81,14 @@ This project gives a [__CMakelists.txt__](./CMakeLists.txt) for you to create pr
|
||||||
|
|
||||||
### __`Linux/macOS/Unix`__
|
### __`Linux/macOS/Unix`__
|
||||||
|
|
||||||
> make nasal
|
> mkdir build
|
||||||
|
>
|
||||||
|
> make -j4
|
||||||
|
|
||||||
You could choose which compiler you want to use:
|
You could choose which compiler you want to use:
|
||||||
|
|
||||||
> make nasal CXX=...
|
> make nasal CXX=...
|
||||||
|
|
||||||
If you think `-O3` isn't that safe and stable, you could choose:
|
|
||||||
|
|
||||||
> make stable-release
|
|
||||||
|
|
||||||
## __How to Use__
|
## __How to Use__
|
||||||
|
|
||||||

|

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

|

|
||||||
|
|
109
makefile
109
makefile
|
@ -1,81 +1,84 @@
|
||||||
STD=c++17
|
STD=c++17
|
||||||
|
|
||||||
NASAL_OBJECT=\
|
NASAL_OBJECT=\
|
||||||
nasal_err.o\
|
build/nasal_err.o\
|
||||||
nasal_ast.o\
|
build/nasal_ast.o\
|
||||||
ast_visitor.o\
|
build/ast_visitor.o\
|
||||||
ast_dumper.o\
|
build/ast_dumper.o\
|
||||||
nasal_lexer.o\
|
build/nasal_lexer.o\
|
||||||
nasal_parse.o\
|
build/nasal_parse.o\
|
||||||
nasal_import.o\
|
build/nasal_import.o\
|
||||||
optimizer.o\
|
build/optimizer.o\
|
||||||
nasal_opcode.o\
|
build/nasal_opcode.o\
|
||||||
symbol_finder.o\
|
build/symbol_finder.o\
|
||||||
nasal_codegen.o\
|
build/nasal_codegen.o\
|
||||||
nasal_misc.o\
|
build/nasal_misc.o\
|
||||||
nasal_gc.o\
|
build/nasal_gc.o\
|
||||||
nasal_builtin.o\
|
build/nasal_builtin.o\
|
||||||
nasal_vm.o\
|
build/nasal_vm.o\
|
||||||
nasal_dbg.o\
|
build/nasal_dbg.o\
|
||||||
main.o
|
build/main.o
|
||||||
|
|
||||||
# for test
|
# for test
|
||||||
nasal: $(NASAL_OBJECT)
|
nasal: $(NASAL_OBJECT) | build
|
||||||
$(CXX) $(NASAL_OBJECT) -O3 -o nasal -ldl
|
$(CXX) $(NASAL_OBJECT) -O3 -o nasal -ldl
|
||||||
|
|
||||||
nasal.exe: $(NASAL_OBJECT)
|
nasal.exe: $(NASAL_OBJECT) | build
|
||||||
$(CXX) $(NASAL_OBJECT) -O3 -o nasal.exe
|
$(CXX) $(NASAL_OBJECT) -O3 -o nasal.exe
|
||||||
|
|
||||||
main.o: src/main.cpp
|
build:
|
||||||
$(CXX) -std=$(STD) -c -O3 src/main.cpp -fno-exceptions -fPIC -o main.o -I .
|
@ if [ ! -d build ]; then mkdir build; fi
|
||||||
|
|
||||||
nasal_misc.o: src/nasal.h src/nasal_misc.cpp
|
build/main.o: src/nasal.h src/nasal_err.h src/nasal_lexer.h src/main.cpp | build
|
||||||
$(CXX) -std=$(STD) -c -O3 src/nasal_misc.cpp -fno-exceptions -fPIC -o nasal_misc.o -I .
|
$(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
|
build/nasal_misc.o: src/nasal.h src/nasal_misc.cpp | build
|
||||||
$(CXX) -std=$(STD) -c -O3 src/nasal_err.cpp -fno-exceptions -fPIC -o nasal_err.o -I .
|
$(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
|
build/nasal_err.o: src/nasal_err.h src/nasal_err.cpp | build
|
||||||
$(CXX) -std=$(STD) -c -O3 src/nasal_gc.cpp -fno-exceptions -fPIC -o nasal_gc.o -I .
|
$(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
|
build/nasal_gc.o: src/nasal_gc.h src/nasal_gc.cpp | build
|
||||||
$(CXX) -std=$(STD) -c -O3 src/nasal_import.cpp -fno-exceptions -fPIC -o nasal_import.o -I .
|
$(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
|
build/nasal_import.o: src/nasal_import.h src/nasal_import.cpp | build
|
||||||
$(CXX) -std=$(STD) -c -O3 src/nasal_lexer.cpp -fno-exceptions -fPIC -o nasal_lexer.o -I .
|
$(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
|
build/nasal_lexer.o: src/nasal_lexer.h src/nasal_lexer.cpp | build
|
||||||
$(CXX) -std=$(STD) -c -O3 src/nasal_ast.cpp -fno-exceptions -fPIC -o nasal_ast.o -I .
|
$(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
|
build/nasal_ast.o: src/nasal_ast.h src/nasal_ast.cpp | build
|
||||||
$(CXX) -std=$(STD) -c -O3 src/nasal_builtin.cpp -fno-exceptions -fPIC -o nasal_builtin.o -I .
|
$(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
|
build/nasal_builtin.o: src/nasal_builtin.h src/nasal_builtin.cpp | build
|
||||||
$(CXX) -std=$(STD) -c -O3 src/nasal_codegen.cpp -fno-exceptions -fPIC -o nasal_codegen.o -I .
|
$(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
|
build/nasal_codegen.o: src/nasal_codegen.h src/nasal_codegen.cpp | build
|
||||||
$(CXX) -std=$(STD) -c -O3 src/nasal_opcode.cpp -fno-exceptions -fPIC -o nasal_opcode.o -I .
|
$(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
|
build/nasal_opcode.o: src/nasal_opcode.h src/nasal_opcode.cpp | build
|
||||||
$(CXX) -std=$(STD) -c -O3 src/nasal_parse.cpp -fno-exceptions -fPIC -o nasal_parse.o -I .
|
$(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
|
build/nasal_parse.o: src/nasal_parse.h src/nasal_parse.cpp src/nasal_ast.h | build
|
||||||
$(CXX) -std=$(STD) -c -O3 src/optimizer.cpp -fno-exceptions -fPIC -o optimizer.o -I .
|
$(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
|
build/optimizer.o: src/optimizer.h src/optimizer.cpp src/nasal_ast.h | build
|
||||||
$(CXX) -std=$(STD) -c -O3 src/symbol_finder.cpp -fno-exceptions -fPIC -o symbol_finder.o -I .
|
$(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
|
build/symbol_finder.o: src/symbol_finder.h src/symbol_finder.cpp src/nasal_ast.h | build
|
||||||
$(CXX) -std=$(STD) -c -O3 src/ast_visitor.cpp -fno-exceptions -fPIC -o ast_visitor.o -I .
|
$(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
|
build/ast_visitor.o: src/nasal_ast.h src/ast_visitor.h src/ast_visitor.cpp | build
|
||||||
$(CXX) -std=$(STD) -c -O3 src/ast_dumper.cpp -fno-exceptions -fPIC -o ast_dumper.o -I .
|
$(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
|
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/nasal_vm.cpp -fno-exceptions -fPIC -o nasal_vm.o -I .
|
$(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
|
build/nasal_vm.o: src/nasal_vm.h src/nasal_vm.cpp | build
|
||||||
$(CXX) -std=$(STD) -c -O3 src/nasal_dbg.cpp -fno-exceptions -fPIC -o nasal_dbg.o -I .
|
$(CXX) -std=$(STD) -c -O3 src/nasal_vm.cpp -fno-exceptions -fPIC -o build/nasal_vm.o -I .
|
||||||
|
|
||||||
|
build/nasal_dbg.o: src/nasal_dbg.h src/nasal_dbg.cpp | build
|
||||||
|
$(CXX) -std=$(STD) -c -O3 src/nasal_dbg.cpp -fno-exceptions -fPIC -o build/nasal_dbg.o -I .
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
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
|
dynamic_libs_dll = libfib.dll libkey.dll libnasock.dll libmat.dll
|
||||||
|
|
||||||
used_header = ../src/nasal.h ../src/nasal_gc.h
|
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
|
STD=c++17
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,7 @@ void execute(
|
||||||
}
|
}
|
||||||
|
|
||||||
// run
|
// run
|
||||||
auto start=clk::now();
|
auto start = clk::now();
|
||||||
if (cmd&VM_DEBUG) {
|
if (cmd&VM_DEBUG) {
|
||||||
dbg(err).run(gen, ld, argv);
|
dbg(err).run(gen, ld, argv);
|
||||||
} else if (cmd&VM_TIME || cmd&VM_EXEC) {
|
} else if (cmd&VM_TIME || cmd&VM_EXEC) {
|
||||||
|
@ -144,7 +144,7 @@ void execute(
|
||||||
|
|
||||||
// get running time
|
// get running time
|
||||||
if (cmd&VM_TIME) {
|
if (cmd&VM_TIME) {
|
||||||
f64 tm=(clk::now()-start).count()*1.0/den;
|
f64 tm = (clk::now()-start).count()*1.0/den;
|
||||||
std::clog << "process exited after " << tm << "s.\n\n";
|
std::clog << "process exited after " << tm << "s.\n\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
enum class expr_type:u32 {
|
enum class expr_type:u32 {
|
||||||
ast_null=0, // null node
|
ast_null = 0, // null node
|
||||||
ast_file_info, // stores file info
|
ast_file_info, // stores file info
|
||||||
ast_block, // code block
|
ast_block, // code block
|
||||||
ast_nil, // nil keyword
|
ast_nil, // nil keyword
|
||||||
|
@ -25,9 +25,9 @@ enum class expr_type:u32 {
|
||||||
ast_callf, // id()
|
ast_callf, // id()
|
||||||
ast_subvec, // id[index:index]
|
ast_subvec, // id[index:index]
|
||||||
ast_param, // function parameter
|
ast_param, // function parameter
|
||||||
ast_ternary,
|
ast_ternary, // ternary operator
|
||||||
ast_binary,
|
ast_binary, // binary operator
|
||||||
ast_unary,
|
ast_unary, // unary operator
|
||||||
ast_for, // for keyword
|
ast_for, // for keyword
|
||||||
ast_forei, // foreach or forindex loop
|
ast_forei, // foreach or forindex loop
|
||||||
ast_while, // while
|
ast_while, // while
|
||||||
|
@ -35,10 +35,10 @@ enum class expr_type:u32 {
|
||||||
ast_cond, // mark a sub-tree of conditional expression
|
ast_cond, // mark a sub-tree of conditional expression
|
||||||
ast_if, // if keyword
|
ast_if, // if keyword
|
||||||
ast_multi_id, // multi identifiers sub-tree
|
ast_multi_id, // multi identifiers sub-tree
|
||||||
ast_tuple,
|
ast_tuple, // tuple, stores multiple scalars
|
||||||
ast_def, // definition
|
ast_def, // definition
|
||||||
ast_assign,
|
ast_assign, // assignment
|
||||||
ast_multi_assign,
|
ast_multi_assign,// multiple assignment
|
||||||
ast_continue, // continue keyword, only used in loop
|
ast_continue, // continue keyword, only used in loop
|
||||||
ast_break, // break keyword, only used in loop
|
ast_break, // break keyword, only used in loop
|
||||||
ast_ret // return keyword, only used in function block
|
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;
|
fileindex = 0;
|
||||||
file = import.filelist().data();
|
file = import.filelist().data();
|
||||||
in_iterloop.push(0);
|
in_iterloop.push(0);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// 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
|
find_symbol(parse.tree()); // search symbols first
|
||||||
gen(op_intg, global.size(), 0);
|
gen(op_intg, global.size(), 0);
|
||||||
block_gen(parse.tree()); // generate main block
|
block_gen(parse.tree()); // generate main block
|
||||||
|
|
|
@ -643,6 +643,12 @@ inline void vm::o_callfv() {
|
||||||
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) {
|
||||||
|
// 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
|
// should reset stack top after allocating vector
|
||||||
// because this may cause gc
|
// because this may cause gc
|
||||||
|
@ -906,6 +912,9 @@ 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;
|
||||||
|
|
|
@ -12,4 +12,13 @@ globals.test_func = func() {
|
||||||
}
|
}
|
||||||
println();
|
println();
|
||||||
println(globals.test_func);
|
println(globals.test_func);
|
||||||
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