add special variable "arg"

This commit is contained in:
ValKmjolnir 2023-07-09 00:59:17 +08:00
parent fd614a132c
commit 3509655424
11 changed files with 849 additions and 443 deletions

View File

@ -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__
![usage](./doc/gif/help.gif)

View File

@ -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
## __使用方法__
![usage](../doc/gif/help.gif)

109
makefile
View File

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

View File

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

View File

@ -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";
}
}

View File

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

View File

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

View File

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

View File

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

378
test/sample.nas Normal file
View File

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