71 Commits
v7.0 ... v8.0

Author SHA1 Message Date
ValKmjolnir
980350d70a prepare for function-call optimization 2022-02-12 18:03:50 +08:00
ValKmjolnir
0ccd3c9bd0 update test/calc.nas & print function of nasal_vec/nasal_hash 2022-02-11 17:04:27 +08:00
ValKmjolnir
3e7ba4d774 little update 2022-02-10 22:20:18 +08:00
ValKmjolnir
a176022840 add notes in lib.nas & add runtime.gc() & add total instruction count in '-o'. 2022-02-09 15:53:09 +08:00
ValKmjolnir
24a1e39ad3 add garbage collector and memory allocator info. use '-d' to activate this function. 2022-02-08 23:40:52 +08:00
ValKmjolnir
a09c6ae2f9 update/optimize test files 2022-02-07 23:41:50 +08:00
ValKmjolnir
2a85e92d4a optimize test file & little update. 2022-02-06 19:55:45 +08:00
ValKmjolnir
92646840e4 reuse codes in nasal_vec::print and nasal_hash::print
now they all use nasal_ref::print
2022-02-05 23:55:56 +08:00
ValKmjolnir
c55ce758ed change increment arguments to a more efficiency level & change gc.nil, gc.one, gc.zero to constant nil, one, zero 2022-02-04 01:51:30 +08:00
ValKmjolnir
aa301aefc3 change increment argument in nasal_gc, because new vm doesn't need to alloc new objects frequently.
vm_str  2048 -> 512

vm_func 1024 -> 512

vm_vec  8192 -> 512
2022-02-03 22:43:51 +08:00
ValKmjolnir
d6d90ab7c8 change 'global' & 'local' in codegen to std::unordered_map & std::vector<std::unordered_map>> 2022-02-02 23:52:00 +08:00
ValKmjolnir
bf780514e6 add constant string calculation in optimizer 2022-02-01 21:20:36 +08:00
ValKmjolnir
eaa54035ff change name of enum:ast_hashmember->ast_pair, ast_new_iter->ast_iter
change ast_hashmember to ast_pair is because this type in fact is the same as std::pair<std::string,nasal_ref> in C++
2022-01-31 17:22:44 +08:00
ValKmjolnir
baa4f5a258 change error info of native function __builtin_import 2022-01-31 14:48:51 +08:00
ValKmjolnir
df24db5b58 optimize nasal_import::check_import 2022-01-30 23:26:30 +08:00
ValKmjolnir
4def93b4ad bug fixed in nasal_vec::print and nasal_hash::print
if vec's or hash's size is 0, a bug will occur because ++depth. now the ++depth has been put behind the size()
2022-01-29 19:51:43 +08:00
ValKmjolnir
0a407437a4 change native function num()
num now returns nil if the argument is not a number or numerable string.
2022-01-27 21:41:06 +08:00
ValKmjolnir
05ab4640da add notes of some native functions in lib.nas 2022-01-26 23:51:25 +08:00
ValKmjolnir
b92eb4b089 update README.md 2022-01-25 15:02:57 +08:00
ValKmjolnir
5778d1e38d update new test data 2022-01-24 15:19:27 +08:00
ValKmjolnir
479e5a2c52 fix errors in README.md 2022-01-22 22:19:14 +08:00
ValKmjolnir
2f455c52c1 fix error in README.md 2022-01-22 22:09:00 +08:00
ValKmjolnir
78c1f9b7a9 add contents in README.md 2022-01-22 22:05:42 +08:00
ValKmjolnir
c68b4c5947 add new option -op & --optimize to use optimizer | delete bytecode op_pone & op_pzero 2022-01-22 13:37:49 +08:00
ValKmjolnir
40344455e6 add optimizer
optimizer now does one work: calculate const number before generating bytecode
2022-01-22 00:41:08 +08:00
ValKmjolnir
630c99c39a optimize codes 2022-01-16 22:48:00 +08:00
ValKmjolnir
46716620e3 add test file 'turingmachine.nas' & change output format of ast & bug fixed 2021-12-28 20:23:47 +08:00
ValKmjolnir
70a43c2f03 bug fixed 2021-12-25 15:56:08 +08:00
ValKmjolnir
1923fc74e4 update README.md 2021-12-23 21:46:53 +08:00
ValKmjolnir
f0ae63bce5 lexer,parser,import,codegen use the same error module. 2021-12-23 21:15:50 +08:00
ValKmjolnir
30650bb64f update error info (except parser)
prepare for nasal_err module
2021-12-23 14:09:54 +08:00
ValKmjolnir
d87aef82b7 debug info now shows both source code and bytecode 2021-12-21 19:57:23 +08:00
ValKmjolnir
e79d1eb8a4 add debugger function: break point & next step 2021-12-21 15:27:38 +08:00
ValKmjolnir
189d49fa4a add debugger framework
with function:backtrace,run,help,show global/local/upvalue,exit
2021-12-20 21:33:22 +08:00
Li Haokun
6a543f2aa7 Merge pull request #7 from sidi762/master
Update c-cpp.yml to solve failures
2021-12-17 00:31:16 +08:00
ValKmjolnir
c27c5b70ee little update 2021-12-16 23:53:13 +08:00
Sidi Liang
0a246d2fc7 Merge branch 'ValKmjolnir:master' into master 2021-12-13 08:49:45 +08:00
Sidi Liang
0956a08bec Update c-cpp.yml 2021-12-13 08:46:20 +08:00
ValKmjolnir
5a80258d20 bug fixed & add os.platform 2021-12-08 18:46:32 +08:00
Li Haokun
85bb502191 Merge pull request #6 from sidi762/master
New Github Action for nightly builds on macOS
2021-12-07 10:37:39 +08:00
Sidi Liang
ead49a657e Change the asset name to 'nasal' 2021-12-06 23:00:25 +08:00
Sidi Liang
df5be35af8 Attempt to make nightly builds for macOS 2021-12-06 22:55:18 +08:00
Sidi Liang
19d5952210 Update c-cpp.yml 2021-12-06 22:47:37 +08:00
Sidi Liang
afd87da5e7 Try out GitHub Actions 2021-12-06 22:36:08 +08:00
ValKmjolnir
9861ecd03e add dylib.dlopen dylib.dlsym dylib.dlclose dylib.dlcall
now you could add your own modules into nasal without changing the source code!
2021-12-03 19:31:03 +08:00
ValKmjolnir
aa191a9feb delete ret stack/ add math.lg
now return address is stored on value stack
2021-12-02 22:23:22 +08:00
ValKmjolnir
b8ef3cf6b6 delete op_cntpop & counter stack
now the iterator will be type vm_cnt and be stored on value stack.
2021-11-25 18:13:31 +08:00
ValKmjolnir
0a8655eb4d fixed bug of in_foreach and in_forindex 2021-11-22 14:24:28 +08:00
ValKmjolnir
52b49edbcf add unix.isdir and unix.isfile 2021-11-15 22:47:52 +08:00
ValKmjolnir
6a35c58df4 2 bugs fixed:
empty string will be true in conditional expressions,but now it is false(and string that is not empty and is not numerable will be true)

foreach(var i;func(){return []}()); will cause sigsegv because codegen generates error op_pop and op_cntpop,now the counter in_foreach and in_forindex change after generating
foreach/forindex expression before block generation.
2021-11-14 23:05:34 +08:00
ValKmjolnir
cd08b2d1bb change code structure 2021-11-02 22:44:42 +08:00
ValKmjolnir
f8e2918561 add unix.opendir unix.readdir unix.closedir 2021-10-31 23:11:04 +08:00
ValKmjolnir
e4ea34db51 add unix.time unix.chdir unix.sleep unix.getcwd unix.getenv 2021-10-29 19:52:49 +08:00
ValKmjolnir
4bfce37f40 add bits lib 2021-10-28 21:49:08 +08:00
ValKmjolnir
fd0d836c03 add io lib & bug fixed 2021-10-27 23:05:25 +08:00
ValKmjolnir
183446d32a bug fixed 2021-10-26 22:34:02 +08:00
ValKmjolnir
540aeb73f4 optimize nasal_ast and fix bug in opr_slc2 2021-10-20 20:54:23 +08:00
ValKmjolnir
4f0acc4d63 fix dynamic para error 2021-10-18 22:05:31 +08:00
ValKmjolnir
56280db2c7 fix sigsegv error 2021-10-18 21:50:25 +08:00
ValKmjolnir
885b57cd52 add upvalue info into detail crash info 2021-10-18 19:59:41 +08:00
ValKmjolnir
bbee31ea55 add detail crash info 2021-10-17 22:57:45 +08:00
ValKmjolnir
1bfa7d2638 update 2021-10-16 23:36:43 +08:00
ValKmjolnir
d4a9412947 optimize code structure 2021-10-16 21:08:57 +08:00
ValKmjolnir
1b240b293e update variable name in nasal_lexer 2021-10-16 14:07:55 +08:00
ValKmjolnir
e41f728589 update 2021-10-15 22:21:57 +08:00
ValKmjolnir
577546763f change function name and cli format 2021-10-14 23:22:28 +08:00
ValKmjolnir
58ea303202 complete simple tutorial 2021-10-14 13:42:07 +08:00
ValKmjolnir
818685c48d change output format of information of bytecodes 2021-10-13 22:59:15 +08:00
ValKmjolnir
5d13261516 optimize source code 2021-10-12 18:26:10 +08:00
ValKmjolnir
56289b5d22 fully functional closure & add benchmark 2021-10-10 14:29:23 +08:00
ValKmjolnir
1733ac0573 vm_nil,vm_num changed to no-gcobject 2021-10-08 23:18:26 +08:00
42 changed files with 11164 additions and 4485 deletions

34
.github/workflows/c-cpp.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: C/C++ CI
on:
schedule:
- cron: "0 16 * * *"
push:
branches: [ master ]
pull_request:
branches: [ master ]
workflow_dispatch:
jobs:
build:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2
- name: make
run: make
- name: Release file
# You may pin to the exact commit or the version.
# uses: djnicholson/release-action@e9a535b3eced09c460e07a84118fb74ae9b53236
uses: marvinpinto/action-automatic-releases@v1.2.1
with:
# GitHub auth token
repo_token: ${{ secrets.GITHUB_TOKEN }}
# Name of Release to add file to
title: macOS Nightly build
# Name of the tag for the release (will be associated with current branch)
automatic_release_tag: next
# File to release
files: nasal

4
.gitignore vendored
View File

@@ -30,3 +30,7 @@
*.exe
*.out
*.app
nasal
.vscode
dump

1080
README.md

File diff suppressed because it is too large Load Diff

395
lib.nas
View File

@@ -1,149 +1,318 @@
var import=func(filename)
{
# lib.nas
# import is used to link another file, this lib function will do nothing.
# because nasal_import will recognize this and link files before generating bytecode.
var import=func(filename){
return __builtin_import(filename);
}
var print=func(elems...)
{
# print is used to print all things in nasal, try and see how it works.
# this function uses std::cout/printf to output logs.
var print=func(elems...){
return __builtin_print(elems);
};
var println=func(elems...)
{
}
# append is used to add values into a vector.
var append=func(vec,elems...){
return __builtin_append(vec,elems);
}
# setsize is used to change the size of vector.
# if the size is larger than before,
# this function will fill vm_nil into uninitialized space.
var setsize=func(vec,size){
return __builtin_setsize(vec,size);
}
# system has the same use in C.
var system=func(str){
return __builtin_system(str);
}
# input uses std::cin and returns what we input.
var input=func(){
return __builtin_input();
}
# split a string by delimiter for example:
# split("ll","hello world") -> ["he","o world"]
# this function will return a vector.
var split=func(deli,str){
return __builtin_split(deli,str);
}
# rand has the same function as the rand in C
# if seed is nil, it will return the random number.
# if seed is not nil, it will be initialized by this seed.
var rand=func(seed=nil){
return __builtin_rand(seed);
}
# id will return the pointer of an gc-object.
# if this object is not managed by gc, it will return 0.
var id=func(object){
return __builtin_id(object);
}
# int will get the integer of input number.
# but carefully use it, because int has range between -2147483648~2147483647
var int=func(val){
return __builtin_int(val);
}
# num will change all the other types into number.
# mostly used to change a numerable string.
var num=func(val){
return __builtin_num(val);
}
# pop used to pop the last element in a vector.
# this function will return the value that poped if vector has element(s).
# if the vector is empty, it will return nil.
var pop=func(vec){
return __builtin_pop(vec);
}
# str is used to change number into string.
var str=func(num){
return __builtin_str(num);
}
# size can get the size of a string/vector/hashmap.
# in fact it can also get the size of number, and the result is the number itself.
# so don't do useless things, though it really works.
var size=func(object){
return __builtin_size(object);
}
# contains is used to check if a key exists in a hashmap/dict.
var contains=func(hash,key){
return __builtin_contains(hash,key);
}
# delete is used to delete a pair in a hashmap/dict by key.
var delete=func(hash,key){
return __builtin_delete(hash,key);
}
# keys is used to get all keys in a hashmap/dict.
# this function will return a vector.
var keys=func(hash){
return __builtin_keys(hash);
}
# time has the same function in C.
var time=func(begin){
return __builtin_time(begin);
}
# die is a special native function.
# use it at where you want the program to crash immediately.
var die=func(str){
return __builtin_die(str);
}
# typeof is used to get the type of an object.
# this function returns a string.
var typeof=func(object){
return __builtin_type(object);
}
# substr will get the sub-string.
# it gets the string, the begin index and sub-string's length as arguments.
var substr=func(str,begin,len){
return __builtin_substr(str,begin,len);
}
# streq is used to compare if two strings are the same.
var streq=func(a,b){
return __builtin_streq(a,b);
}
# left is used to get the sub-string like substr.
# but the begin index is 0.
var left=func(str,len){
return __builtin_left(str,len);
}
# right i used to get the sub-string like substr.
# but the begin index is strlen-len.
var right=func(str,len){
return __builtin_right(str,len);
}
# cmp is used to compare two strings.
# normal string will not be correctly compared by operators < > <= >=
# because these operators will turn strings into numbers then compare.
var cmp=func(a,b){
return __builtin_cmp(a,b);
}
# chr is used to get the character by ascii-number.
# for example chr(65) -> 'A'
var chr=func(code){
return __builtin_chr(code);
}
# println has the same function as print.
# but it will output a '\n' after using print.
var println=func(elems...){
__builtin_print(elems);
elems=['\n'];
return __builtin_print(elems);
}
var append=func(vec,elems...)
{
return __builtin_append(vec,elems);
}
var setsize=func(vec,size)
{
return __builtin_setsize(vec,size);
}
var system=func(str)
{
return __builtin_system(str);
}
var input=func()
{
return __builtin_input();
}
var sleep=func(duration)
{
return __builtin_sleep(duration);
}
var split=func(deli,str)
{
return __builtin_split(deli,str);
}
var rand=func(seed=nil)
{
return __builtin_rand(seed);
}
var id=func(object)
{
return __builtin_id(object);
}
var int=func(val)
{
return __builtin_int(val);
}
var num=func(val)
{
return __builtin_num(val);
}
var pop=func(vec)
{
return __builtin_pop(vec);
}
var str=func(num)
{
return __builtin_str(num);
}
var size=func(object)
{
return __builtin_size(object);
}
var contains=func(hash,key)
{
return __builtin_contains(hash,key);
}
var delete=func(hash,key)
{
return __builtin_delete(hash,key);
}
var keys=func(hash)
{
return __builtin_keys(hash);
}
var time=func(begin_time)
{
return __builtin_time(begin_time);
}
var die=func(str)
{
return __builtin_die(str);
}
var typeof=func(object)
{
return __builtin_type(object);
}
var substr=func(str,begin,len)
{
return __builtin_substr(str,begin,len);
}
var streq=func(a,b)
{
return __builtin_streq(a,b);
}
var left=func(str,len)
{
return __builtin_left(str,len);
}
var right=func(str,len)
{
return __builtin_right(str,len);
}
var cmp=func(a,b)
{
return __builtin_cmp(a,b);
}
var chr=func(code)
{
return __builtin_chr(code);
}
var io=
{
fin: func(filename){return __builtin_fin(filename);},
fout:func(filename,str){return __builtin_fout(filename,str);}
SEEK_SET:0,
SEEK_CUR:1,
SEEK_END:2,
# get content of a file by filename. returns a string.
fin: func(filename){return __builtin_fin(filename);},
# input a string as the content of a file.
fout: func(filename,str){return __builtin_fout(filename,str);},
# same as C fopen. open file and get the FILE*.
open: func(filename,mode="r"){return __builtin_open(filename,mode);},
# same as C fclose. close file by FILE*.
close: func(filehandle){return __builtin_close(filehandle);},
# same as C fread. read file by FILE*.
read: func(filehandle,buf,len){return __builtin_read(filehandle,buf,len);},
# same as C fwrite. write file by FILE*.
write: func(filehandle,str){return __builtin_write(filehandle,str);},
# same as C fseek. seek place by FILE*.
seek: func(filehandle,pos,whence){return __builtin_seek(filehandle,pos,whence);},
# same as C ftell.
tell: func(filehandle){return __builtin_tell(filehandle);},
# read file by lines. use FILE*.
# get nil if EOF
readln:func(filehandle){return __builtin_readln(filehandle);},
# same as C stat.
stat: func(filename){return __builtin_stat(filename);},
# same as C feof. check if FILE* gets the end of file(EOF).
eof: func(filehandle){return __builtin_eof(filehandle);}
};
# get file status. using data from io.stat
var fstat=func(filename){
var s=io.stat(filename);
return {
st_dev: s[0],
st_ino: s[1],
st_mode: s[2],
st_nlink:s[3],
st_uid: s[4],
st_gid: s[5],
st_rdev: s[6],
st_size: s[7],
st_atime:s[8],
st_mtime:s[9],
st_ctime:s[10]
};
}
# functions that do bitwise calculation.
# carefully use it, all the calculations are based on integer.
var bits=
{
# xor
bitxor: func(a,b){return __builtin_xor(a,b); },
# and
bitand: func(a,b){return __builtin_and(a,b); },
# or
bitor: func(a,b){return __builtin_or(a,b); },
# nand
bitnand: func(a,b){return __builtin_nand(a,b);},
bitnot: func(a) {return __builtin_not(a); }
# not
bitnot: func(a) {return __builtin_not(a); },
# get bit data from a special string. for example:
# bits.fld(s,0,3);
# if s stores 10100010(162)
# will get 101(5).
fld: func(str,startbit,len){return __builtin_fld;},
# get sign-extended data from a special string. for example:
# bits.sfld(s,0,3);
# if s stores 10100010(162)
# will get 101(5) then this will be signed extended to
# 11111101(-3).
sfld: func(str,startbit,len){return __builtin_sfld;},
# set value into a special string to store it. little-endian, for example:
# bits.setfld(s,0,8,69);
# set 01000101(69) to string will get this:
# 10100010(162)
# so s[0]=162.
setfld: func(str,startbit,len,val){return __builtin_setfld;},
# get a special string filled by '\0' to use in setfld.
buf: func(len){return __builtin_buf;}
};
# mostly used math functions and special constants, you know.
var math=
{
e: 2.7182818284590452354,
pi: 3.14159265358979323846264338327950288,
inf: 1/0,
nan: 0/0,
sin: func(x) {return __builtin_sin(x); },
cos: func(x) {return __builtin_cos(x); },
tan: func(x) {return __builtin_tan(x); },
exp: func(x) {return __builtin_exp(x); },
exp: func(x) {return __builtin_exp(x); },
lg: func(x) {return __builtin_lg(x); },
ln: func(x) {return __builtin_ln(x); },
sqrt: func(x) {return __builtin_sqrt(x); },
atan2: func(x,y){return __builtin_atan2(x,y);},
inf: 1/0,
nan: 0/0,
isnan: func(x) {return __builtin_isnan(x); }
};
var unix=
{
pipe: func(){die("not supported yet");},
fork: func(){die("not supported yet");},
dup2: func(fd0,fd1){die("not supported yet");},
exec: func(filename,argv,envp){die("not supported yet");},
waitpid: func(pid,nohang=0){die("not supported yet");},
isdir: func(path){return bits.bitand(io.stat(path)[2],0x4000);}, # S_IFDIR 0x4000
isfile: func(path){return bits.bitand(io.stat(path)[2],0x8000);}, # S_IFREG 0x8000
opendir: func(path){return __builtin_opendir;},
readdir: func(handle){return __builtin_readdir;},
closedir: func(handle){return __builtin_closedir;},
time: func(){return time(0);},
sleep: func(secs){return __builtin_sleep(secs);},
chdir: func(path){return __builtin_chdir(path);},
environ: func(){die("not supported yet");},
getcwd: func(){return __builtin_getcwd();},
getenv: func(envvar){return __builtin_getenv(envvar);}
};
# dylib is the core hashmap for developers to load their own library.
var dylib=
{
# open dynamic lib.
dlopen: func(libname){return __builtin_dlopen;},
# load symbol from an open dynamic lib.
dlsym: func(lib,sym){return __builtin_dlsym; },
# close dynamic lib, this operation will make all the symbols loaded from it invalid.
dlclose: func(lib){return __builtin_dlclose; },
# call the loaded symbol.
dlcall: func(funcptr,args...){return __builtin_dlcall}
};
# os is used to use or get some os-related info/functions.
# windows/macOS/linux are supported.
var os=
{
# get a string that tell which os it runs on.
platform: func(){return __builtin_platform;}
};
# runtime gives us some functions that we could manage it manually.
var runtime=
{
# do garbage collection manually.
# carefully use it because using it frequently may make program running slower.
gc: func(){return __builtin_gc;}
};
# important global constants
var D2R=math.pi/180;
var FPS2KT=0.5925;
var FT2M=0.3048;

304
main.cpp
View File

@@ -1,144 +1,162 @@
#include "nasal.h"
void help_cmd()
{
std::cout
#ifdef _WIN32
<<"use command \'chcp 65001\' if want to use unicode.\n"
#endif
<<"nasal [option]\n"
<<"option:\n"
<<" -h, --help | get help.\n"
<<" -v, --version | get version of nasal interpreter.\n\n"
<<"nasal [file]\n"
<<"file:\n"
<<" input file name to execute script file.\n\n"
<<"nasal [option] [file]\n"
<<"option:\n"
<<" -l, --lex | view token info.\n"
<<" -a, --ast | view abstract syntax tree.\n"
<<" -c, --code | view bytecode.\n"
<<" -e, --exec | execute script file.\n"
<<" -t, --time | execute and get the running time.\n"
<<" -o, --opcnt | count operands while running.\n"
<<"file:\n"
<<" input file name to execute script file.\n";
return;
}
void logo()
{
std::cout
<<" __ _ \n"
<<" /\\ \\ \\__ _ ___ __ _| | \n"
<<" / \\/ / _` / __|/ _` | | \n"
<<" / /\\ / (_| \\__ \\ (_| | | \n"
<<" \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
<<"nasal interpreter ver 7.0\n"
<<"thanks to : https://github.com/andyross/nasal\n"
<<"code repo : https://github.com/ValKmjolnir/Nasal-Interpreter\n"
<<"code repo : https://gitee.com/valkmjolnir/Nasal-Interpreter\n"
<<"lang info : http://wiki.flightgear.org/Nasal_scripting_language\n"
<<"input \"nasal -h\" to get help .\n";
return;
}
void die(const char* stage,const std::string& filename)
{
std::cout<<"["<<stage<<"] in <"<<filename<<">: error(s) occurred,stop.\n";
std::exit(1);
return;
}
void execute(const std::string& file,const std::string& command)
{
nasal_lexer lexer;
nasal_parse parse;
nasal_import import;
nasal_codegen codegen;
nasal_vm vm;
lexer.openfile(file);
lexer.scanner();
if(lexer.get_error())
die("lexer",file);
if(command=="--lex" || command=="-l")
{
lexer.print_token();
return;
}
parse.main_process(lexer.get_token_list());
if(parse.get_error())
die("parse",file);
if(command=="--ast" || command=="-a")
{
parse.get_root().print_ast(0);
return;
}
// first used file is itself
import.link(parse.get_root(),file);
if(import.get_error())
die("import",file);
codegen.main_progress(import.get_root(),import.get_file());
if(codegen.get_error())
die("code",file);
if(command=="--code" || command=="-c")
{
codegen.print_byte_code();
return;
}
vm.init(
codegen.get_str_table(),
codegen.get_num_table(),
import.get_file()
);
if(command=="--exec" || command=="-e" || command=="--opcnt" || command=="-o")
vm.run(codegen.get_exec_code(),command=="--opcnt" || command=="-o");
else if(command=="--time" || command=="-t")
{
clock_t begin=clock();
vm.run(codegen.get_exec_code(),false);
std::cout<<"process exited after "<<((double)(clock()-begin))/CLOCKS_PER_SEC<<"s.\n";
}
vm.clear();
return;
}
int main(int argc,const char* argv[])
{
std::string command,file;
if(argc==2 && (!strcmp(argv[1],"-v") || !strcmp(argv[1],"--version")))
logo();
else if(argc==2 && (!strcmp(argv[1],"-h") || !strcmp(argv[1],"--help")))
help_cmd();
else if(argc==2 && argv[1][0]!='-')
{
file=argv[1];
command="-e";
execute(file,command);
}
else if(argc==3 &&
(!strcmp(argv[1],"--lex") ||
!strcmp(argv[1],"-l") ||
!strcmp(argv[1],"--ast") ||
!strcmp(argv[1],"-a") ||
!strcmp(argv[1],"--code") ||
!strcmp(argv[1],"-c") ||
!strcmp(argv[1],"--exec") ||
!strcmp(argv[1],"-e") ||
!strcmp(argv[1],"--opcnt")||
!strcmp(argv[1],"-o") ||
!strcmp(argv[1],"--time") ||
!strcmp(argv[1],"-t")))
{
file=argv[2];
command=argv[1];
execute(file,command);
}
else
{
std::cout
<<"invalid argument(s).\n"
<<"use nasal -h to get help.\n";
exit(1);
}
return 0;
#include "nasal.h"
const uint32_t VM_LEXINFO =1;
const uint32_t VM_ASTINFO =2;
const uint32_t VM_CODEINFO =4;
const uint32_t VM_EXECTIME =8;
const uint32_t VM_OPCALLNUM=16;
const uint32_t VM_EXEC =32;
const uint32_t VM_DBGINFO =64;
const uint32_t VM_DEBUG =128;
const uint32_t VM_OPTIMIZE =256;
void help()
{
std::cout
#ifdef _WIN32
<<"use command \'chcp 65001\' if want to use unicode.\n"
#endif
<<"nasal <option>\n"
<<"option:\n"
<<" -h, --help | get help.\n"
<<" -v, --version | get version of nasal interpreter.\n\n"
<<"nasal <file>\n"
<<"file:\n"
<<" input file name to execute script file.\n\n"
<<"nasal [options...] <file>\n"
<<"option:\n"
<<" -l, --lex | view token info.\n"
<<" -a, --ast | view abstract syntax tree.\n"
<<" -c, --code | view bytecode.\n"
<<" -e, --exec | execute.\n"
<<" -t, --time | execute and get the running time.\n"
<<" -o, --opcnt | execute and count used operands.\n"
<<" -d, --detail | execute and get detail crash info.\n"
<<" | get garbage collector info if didn't crash.\n"
<<" -op, --optimize| use optimizer(beta).\n"
<<" | if want to use -op and run, please use -op -e/-t/-o/-d.\n"
<<" -dbg, --debug | debug mode (this will ignore -t -o -d).\n"
<<"file:\n"
<<" input file name to execute script file.\n";
}
void logo()
{
std::cout
<<" __ _ \n"
<<" /\\ \\ \\__ _ ___ __ _| | \n"
<<" / \\/ / _` / __|/ _` | | \n"
<<" / /\\ / (_| \\__ \\ (_| | | \n"
<<" \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
<<"nasal interpreter ver 8.0\n"
<<"thanks to : https://github.com/andyross/nasal\n"
<<"code repo : https://github.com/ValKmjolnir/Nasal-Interpreter\n"
<<"code repo : https://gitee.com/valkmjolnir/Nasal-Interpreter\n"
<<"lang info : http://wiki.flightgear.org/Nasal_scripting_language\n"
<<"input <nasal -h> to get help .\n";
}
void err()
{
std::cout
<<"invalid argument(s).\n"
<<"use <nasal -h> to get help.\n";
std::exit(1);
}
void execute(const std::string& file,const uint32_t cmd)
{
// front end use the same error module
nasal_err nerr;
nasal_lexer lexer(nerr);
nasal_parse parse(nerr);
nasal_import linker(nerr);
nasal_codegen gen(nerr);
// back end
nasal_vm vm;
// lexer scans file to get tokens
lexer.scan(file);
if(cmd&VM_LEXINFO)
lexer.print();
// parser gets lexer's token list to compile
parse.compile(lexer);
// linker gets parser's ast and load import files to this ast
linker.link(parse,file);
if(cmd&VM_ASTINFO)
parse.print();
// optimizer does simple optimization on ast
if(cmd&VM_OPTIMIZE)
optimize(parse.ast());
// code generator gets parser's ast and linker's import file list to generate code
gen.compile(parse,linker);
if(cmd&VM_CODEINFO)
gen.print();
// run bytecode
if(cmd&VM_DEBUG)
{
nasal_dbg debugger;
debugger.run(gen,linker);
}
else if(cmd&VM_EXECTIME)
{
clock_t t=clock();
vm.run(gen,linker,cmd&VM_OPCALLNUM,cmd&VM_DBGINFO);
std::cout<<"process exited after "<<((double)(clock()-t))/CLOCKS_PER_SEC<<"s.\n";
}
else if(cmd&VM_EXEC)
vm.run(gen,linker,cmd&VM_OPCALLNUM,cmd&VM_DBGINFO);
}
int main(int argc,const char* argv[])
{
if(argc<=1)
{
logo();
return 0;
}
if(argc==2)
{
std::string s(argv[1]);
if(s=="-v" || s=="--version")
logo();
else if(s=="-h" || s=="--help")
help();
else if(s[0]!='-')
execute(s,VM_EXEC);
else
err();
return 0;
}
uint32_t cmd=0;
for(int i=1;i<argc-1;++i)
{
std::string s(argv[i]);
if(s=="--lex" || s=="-l")
cmd|=VM_LEXINFO;
else if(s=="--ast" || s=="-a")
cmd|=VM_ASTINFO;
else if(s=="--code" || s=="-c")
cmd|=VM_CODEINFO;
else if(s=="--exec" || s=="-e")
cmd|=VM_EXEC;
else if(s=="--opcnt" || s=="-o")
cmd|=VM_OPCALLNUM|VM_EXEC;
else if(s=="--time" || s=="-t")
cmd|=VM_EXECTIME;
else if(s=="--detail" || s=="-d")
cmd|=VM_DBGINFO|VM_EXEC;
else if(s=="--optimize" || s=="-op")
cmd|=VM_OPTIMIZE;
else if(s=="--debug" || s=="-dbg")
cmd|=VM_DEBUG;
else
err();
}
execute(argv[argc-1],cmd);
return 0;
}

34
makefile Normal file
View File

@@ -0,0 +1,34 @@
.PHONY=test
nasal:main.cpp nasal_ast.h nasal_err.h nasal_builtin.h nasal_opt.h nasal_codegen.h nasal_gc.h nasal_import.h nasal_lexer.h nasal_parse.h nasal_vm.h nasal_dbg.h nasal.h
clang++ -std=c++11 -O3 main.cpp -o nasal -fno-exceptions -ldl -Wshadow -Wall
test:nasal
./nasal -op -e test/ascii-art.nas
./nasal -op -c test/bf.nas
./nasal -op -c test/bfconvertor.nas
./nasal -op -e -d test/bfs.nas
./nasal -op -t test/bigloop.nas
./nasal -op -e test/bp.nas
./nasal -op -e -d test/calc.nas
./nasal -op -e test/choice.nas
./nasal -op -e test/class.nas
./nasal -op -c test/exception.nas
./nasal -op -t -d test/fib.nas
./nasal -op -e test/filesystem.nas
./nasal -op -e -d test/hexdump.nas
./nasal -op -e test/json.nas
./nasal -op -e test/leetcode1319.nas
./nasal -op -e -d test/lexer.nas
./nasal -op -e -d test/life.nas
./nasal -op -t test/loop.nas
./nasal -op -t -d test/mandel.nas
./nasal -op -t -d test/mandelbrot.nas
./nasal -op -c test/module_test.nas
./nasal -op -e test/nasal_test.nas
./nasal -op -t -d test/pi.nas
./nasal -op -t -d test/prime.nas
./nasal -op -t -d test/quick_sort.nas
./nasal -op -e test/scalar.nas
./nasal -op -e test/trait.nas
./nasal -op -t -d test/turingmachine.nas
./nasal -op -t -d test/ycombinator.nas

30
module/fib.cpp Normal file
View File

@@ -0,0 +1,30 @@
#include <iostream>
#include "../nasal.h"
double fibonaci(double x){
if(x<=2)
return x;
return fibonaci(x-1)+fibonaci(x-2);
}
extern "C" nasal_ref fib(std::vector<nasal_ref>& args,nasal_gc& gc){
std::cout<<"[mod] this is the first test module of nasal\n";
nasal_ref num=args[0];
if(num.type!=vm_num)
return builtin_err("extern_fib","\"num\" must be number");
return {vm_num,fibonaci(num.to_number())};
}
extern "C" nasal_ref quick_fib(std::vector<nasal_ref>& args,nasal_gc& gc){
std::cout<<"[mod] this is the first test module of nasal\n";
nasal_ref num=args[0];
if(num.type!=vm_num)
return builtin_err("extern_quick_fib","\"num\" must be number");
if(num.num()<2)
return num;
double a=1,b=1,res=0;
for(double i=1;i<num.num();i+=1){
res=a+b;
a=b;
b=res;
}
return {vm_num,res};
}

9
module/makefile Normal file
View File

@@ -0,0 +1,9 @@
.PHONY=clean
libfib.so: fib.cpp
clang++ -c -O3 fib.cpp -fPIC -o fib.o
clang++ -shared -o libfib.so fib.o
libfib.dll: fib.cpp
g++ -c -O3 fib.cpp -fPIC -o fib.o
g++ -shared -o libfib.dll fib.o
clean:
rm *.o *.so *.dll *.dylib

45
nasal.h
View File

@@ -1,8 +1,6 @@
#ifndef __NASAL_H__
#define __NASAL_H__
#pragma GCC optimize(2)
#include <stdint.h>
#include <unistd.h>
#include <stdlib.h>
@@ -21,10 +19,16 @@
#include <vector>
#include <unordered_map>
/*
check if a string can be converted to a number
if this string cannot be converted to a number,it will return nan
*/
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif
inline double hex_to_double(const char* str)
{
double ret=0;
@@ -105,33 +109,34 @@ double str2num(const char* str)
return is_negative?-ret_num:ret_num;
}
/*
show raw string
*/
void raw_string(const std::string& str)
std::string rawstr(const std::string& str)
{
std::string ret("");
for(auto i:str)
switch(i)
{
case '\a': std::cout<<"\\a";break;
case '\b': std::cout<<"\\b";break;
case '\f': std::cout<<"\\f";break;
case '\n': std::cout<<"\\n";break;
case '\r': std::cout<<"\\r";break;
case '\t': std::cout<<"\\t";break;
case '\v': std::cout<<"\\v";break;
case '\0': std::cout<<"\\0";break;
default: std::cout<<i; break;
case '\a': ret+="\\a";break;
case '\b': ret+="\\b";break;
case '\f': ret+="\\f";break;
case '\n': ret+="\\n";break;
case '\r': ret+="\\r";break;
case '\t': ret+="\\t";break;
case '\v': ret+="\\v";break;
case '\0': ret+="\\0";break;
default: ret+=i; break;
}
return;
return ret;
}
#include "nasal_err.h"
#include "nasal_lexer.h"
#include "nasal_ast.h"
#include "nasal_parse.h"
#include "nasal_import.h"
#include "nasal_opt.h"
#include "nasal_gc.h"
#include "nasal_builtin.h"
#include "nasal_codegen.h"
#include "nasal_vm.h"
#include "nasal_dbg.h"
#endif

View File

@@ -3,152 +3,246 @@
enum ast_node
{
ast_null=0,
ast_root,ast_block,
ast_file, // ast_file is only used to store which file the subtree is on,codegen will generate nothing
ast_nil,ast_num,ast_str,ast_id,ast_func,ast_hash,ast_vec,
ast_hashmember,ast_call,ast_callh,ast_callv,ast_callf,ast_subvec,
ast_args,ast_default_arg,ast_dynamic_id,
ast_and,ast_or,
ast_equal,ast_addeq,ast_subeq,ast_multeq,ast_diveq,ast_lnkeq,
ast_cmpeq,ast_neq,
ast_less,ast_leq,
ast_grt,ast_geq,
ast_add,ast_sub,ast_mult,ast_div,ast_link,
ast_neg,ast_not,
ast_trino,
ast_for,ast_forindex,ast_foreach,ast_while,ast_new_iter,
ast_conditional,ast_if,ast_elsif,ast_else,
ast_multi_id,ast_multi_scalar,
ast_def,ast_multi_assign,
ast_continue,ast_break,ast_ret
ast_null=0, // null node
ast_root, // mark the root node of ast
ast_block, // expression block
ast_file, // used to store which file the sub-tree is on, only used in main block
ast_nil, // nil keyword
ast_num, // number, basic value type
ast_str, // string, basic value type
ast_id, // identifier
ast_func, // func keyword
ast_hash, // hash, basic value type
ast_vec, // vector, basic value type
ast_pair, // pair of key and value in hashmap
ast_call, // mark a sub-tree of calling an identifier
ast_callh, // id.name
ast_callv, // id[index]
ast_callf, // id()
ast_subvec, // id[index:index]
ast_args, // mark a sub-tree of function parameters
ast_default, // default parameter
ast_dynamic, // dynamic parameter
ast_and, // and keyword
ast_or, // or keyword
ast_equal, // =
ast_addeq, // +=
ast_subeq, // -=
ast_multeq, // *=
ast_diveq, // /=
ast_lnkeq, // ~=
ast_cmpeq, // ==
ast_neq, // !=
ast_less, // <
ast_leq, // <=
ast_grt, // >
ast_geq, // >=
ast_add, // +
ast_sub, // -
ast_mult, // *
ast_div, // /
ast_link, // ~
ast_neg, // -
ast_not, // ~
ast_trino, // ?:
ast_for, // for keyword
ast_forindex, // forindex keyword
ast_foreach, // foreach keyword
ast_while, // while
ast_iter, // iterator, used in forindex/foreach
ast_conditional, // mark a sub-tree of conditional expression
ast_if, // if keyword
ast_elsif, // elsif keyword
ast_else, // else keyword
ast_multi_id, // multi identifiers sub-tree
ast_multi_scalar,// multi value sub-tree
ast_def, // definition
ast_multi_assign,// multi assignment sub-tree
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
};
const char* ast_name[]=
{
"null",
"root","block",
"root",
"block",
"file",
"nil","num","str","id","func","hash","vec",
"hashmember","call","callh","callv","callf","subvec",
"args","deflt_arg","dyn_id",
"and","or",
"=","+=","-=","*=","/=","~=",
"==","!=",
"<","<=",
">",">=",
"+","-","*","/","~",
"unary-","unary!",
"nil",
"num",
"str",
"id",
"func",
"hash",
"vec",
"hashmember",
"call",
"callh",
"callv",
"callf",
"subvec",
"args",
"default",
"dynamic",
"and",
"or",
"=",
"+=",
"-=",
"*=",
"/=",
"~=",
"==",
"!=",
"<",
"<=",
">",
">=",
"+",
"-",
"*",
"/",
"~",
"unary-",
"unary!",
"trino",
"for","forindex","foreach","while","iter",
"conditional","if","elsif","else",
"multi_id","multi_scalar",
"def","multi_assign",
"continue","break","return"
"for",
"forindex",
"foreach",
"while",
"iter",
"conditional",
"if",
"elsif",
"else",
"multi_id",
"multi_scalar",
"def",
"multi_assign",
"continue",
"break",
"return",
nullptr
};
class nasal_ast
{
private:
int line;
int type;
double num;
std::string str;
std::vector<nasal_ast> children;
uint32_t _line;
uint32_t _type;
double _num;
std::string _str;
std::vector<nasal_ast> _child;
public:
nasal_ast(){line=0;type=ast_null;}
nasal_ast(const int l,const int t){line=l;type=t;}
nasal_ast(const uint32_t l=0,const uint32_t t=ast_null):_line(l),_type(t){}
nasal_ast(const nasal_ast&);
nasal_ast(nasal_ast&&);
void print(int,bool);
void clear();
nasal_ast& operator=(const nasal_ast&);
nasal_ast& operator=(nasal_ast&&);
void print_ast(const int);
void clear();
void add_child(nasal_ast&& ast){children.push_back(std::move(ast));}
void add_child(const nasal_ast& ast){children.push_back(ast);}
void set_line(const int l){line=l;}
void set_type(const int t){type=t;}
void set_str(const std::string& s){str=s;}
void set_num(const double n){num=n;}
int get_line(){return line;}
int get_type(){return type;}
double get_num() {return num;}
std::string& get_str(){return str;}
std::vector<nasal_ast>& get_children(){return children;}
nasal_ast& operator[](const int index){return _child[index];}
const nasal_ast& operator[](const int index) const {return _child[index];}
size_t size() const {return _child.size();}
void add(nasal_ast&& ast){_child.push_back(std::move(ast));}
void add(const nasal_ast& ast){_child.push_back(ast);}
void set_line(const uint32_t l){_line=l;}
void set_type(const uint32_t t){_type=t;}
void set_str(const std::string& s){_str=s;}
void set_num(const double n){_num=n;}
int get_line() const {return line;}
int get_type() const {return type;}
double get_num() const {return num;}
const std::string& get_str() const {return str;}
const std::vector<nasal_ast>& get_children() const {return children;}
inline uint32_t line() const {return _line;}
inline uint32_t type() const {return _type;}
inline double num() const {return _num;}
inline const std::string& str() const {return _str;}
inline const std::vector<nasal_ast>& child() const {return _child;}
inline std::vector<nasal_ast>& child(){return _child;}
};
nasal_ast::nasal_ast(const nasal_ast& tmp)
{
line=tmp.line;
type=tmp.type;
num =tmp.num;
str =tmp.str;
children=tmp.children;
return;
_line=tmp._line;
_type=tmp._type;
_num =tmp._num;
_str =tmp._str;
_child=tmp._child;
}
nasal_ast::nasal_ast(nasal_ast&& tmp)
{
line=tmp.line;
type=tmp.type;
num =tmp.num;
str.swap(tmp.str);
children.swap(tmp.children);
return;
_line=tmp._line;
_type=tmp._type;
_num =tmp._num;
_str.swap(tmp._str);
_child.swap(tmp._child);
}
nasal_ast& nasal_ast::operator=(const nasal_ast& tmp)
{
line=tmp.line;
type=tmp.type;
num=tmp.num;
str=tmp.str;
children=tmp.children;
_line=tmp._line;
_type=tmp._type;
_num=tmp._num;
_str=tmp._str;
_child=tmp._child;
return *this;
}
nasal_ast& nasal_ast::operator=(nasal_ast&& tmp)
{
line=tmp.line;
type=tmp.type;
num=tmp.num;
str.swap(tmp.str);
children.swap(tmp.children);
_line=tmp._line;
_type=tmp._type;
_num=tmp._num;
_str.swap(tmp._str);
_child.swap(tmp._child);
return *this;
}
void nasal_ast::clear()
{
line=0;
num=0;
str="";
type=ast_null;
children.clear();
return;
_line=0;
_num=0;
_str="";
_type=ast_null;
_child.clear();
}
void nasal_ast::print_ast(const int depth)
void nasal_ast::print(int depth,bool last=false)
{
for(int i=0;i<depth;++i)
std::cout<<"| ";
std::cout<<ast_name[type];
if(type==ast_str || type==ast_id || type==ast_default_arg || type==ast_dynamic_id || type==ast_callh)
{
std::cout<<":";
raw_string(str);
}
else if(type==ast_num || type==ast_file)
std::cout<<":"<<num;
static std::vector<std::string> intentation={};
for(auto& i:intentation)
std::cout<<i;
std::cout<<ast_name[_type];
if(
_type==ast_str ||
_type==ast_id ||
_type==ast_default ||
_type==ast_dynamic ||
_type==ast_callh)
std::cout<<":"<<rawstr(_str);
else if(_type==ast_num || _type==ast_file)
std::cout<<":"<<_num;
std::cout<<'\n';
for(auto& i:children)
i.print_ast(depth+1);
return;
if(last && depth)
intentation.back()=" ";
else if(!last && depth)
#ifdef _WIN32
intentation.back()="| ";
#else
intentation.back()="";
#endif
for(uint32_t i=0;i<_child.size();++i)
{
#ifdef _WIN32
intentation.push_back(i==_child.size()-1?"`-":"|-");
#else
intentation.push_back(i==_child.size()-1?"└─":"├─");
#endif
_child[i].print(depth+1,i==_child.size()-1);
intentation.pop_back();
}
}
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

306
nasal_dbg.h Normal file
View File

@@ -0,0 +1,306 @@
#ifndef __NASAL_DBG_H__
#define __NASAL_DBG_H__
#include "nasal_vm.h"
class nasal_dbg:public nasal_vm
{
private:
bool next_step;
uint16_t bk_fidx;
uint32_t bk_line;
file_line src;
std::vector<std::string> parse(const std::string&);
uint16_t get_fileindex(const std::string&);
void err();
void help();
void stepinfo();
void interact();
public:
nasal_dbg():
next_step(false),
bk_fidx(0),bk_line(0){}
void run(
const nasal_codegen&,
const nasal_import&
);
};
std::vector<std::string> nasal_dbg::parse(const std::string& cmd)
{
std::vector<std::string> res;
std::string tmp="";
for(uint32_t i=0;i<cmd.length();++i)
{
if(cmd[i]==' ' && tmp.length())
{
res.push_back(tmp);
tmp="";
continue;
}
tmp+=cmd[i];
}
if(tmp.length())
res.push_back(tmp);
return res;
}
uint16_t nasal_dbg::get_fileindex(const std::string& filename)
{
for(uint16_t i=0;i<files_size;++i)
if(filename==files[i])
return i;
return 65535;
}
void nasal_dbg::err()
{
std::cerr
<<"incorrect command\n"
<<"input \'h\' to get help\n";
}
void nasal_dbg::help()
{
std::cout
<<"<option>\n"
<<"\th, help | get help\n"
<<"\tbt, backtrace | get function call trace\n"
<<"\tc, continue | run program until break point or exit\n"
<<"\tg, global | see global values\n"
<<"\tl, local | see local values\n"
<<"\tu, upval | see upvalue\n"
<<"\ta, all | show global,local and upvalue\n"
<<"\tn, next | execute next bytecode\n"
<<"\tq, exit | exit debugger\n"
<<"<option> <filename> <line>\n"
<<"\tbk, break | set break point\n";
}
void nasal_dbg::stepinfo()
{
uint32_t begin,end;
uint32_t line=bytecode[pc].line==0?0:bytecode[pc].line-1;
src.load(files[bytecode[pc].fidx]);
printf("\nsource code:\n");
begin=(line>>3)==0?0:((line>>3)<<3);
end=(1+(line>>3))<<3;
for(uint32_t i=begin;i<end && i<src.size();++i)
printf("%s\t%s\n",i==line?"-->":" ",src[i].c_str());
printf("next bytecode:\n");
begin=(pc>>3)==0?0:((pc>>3)<<3);
end=(1+(pc>>3))<<3;
for(uint32_t i=begin;i<end && bytecode[i].op!=op_exit;++i)
bytecodeinfo(i==pc?"-->\t":" \t",i);
stackinfo(5);
}
void nasal_dbg::interact()
{
// special operand
if(bytecode[pc].op==op_intg)
{
std::cout
<<"[debug] nasal debug mode\n"
<<"input \'h\' to get help\n";
}
else if(bytecode[pc].op==op_nop || bytecode[pc].op==op_exit)
return;
if(
(bytecode[pc].fidx!=bk_fidx || bytecode[pc].line!=bk_line) && // break point
!next_step // next step
)return;
next_step=false;
std::string cmd;
stepinfo();
while(1)
{
printf(">> ");
std::getline(std::cin,cmd);
auto res=parse(cmd);
if(res.size()==1)
{
if(res[0]=="h" || res[0]=="help")
help();
else if(res[0]=="bt" || res[0]=="backtrace")
traceback();
else if(res[0]=="c" || res[0]=="continue")
return;
else if(res[0]=="g" || res[0]=="global")
global_state();
else if(res[0]=="l" || res[0]=="local")
local_state();
else if(res[0]=="u" || res[0]=="upval")
upval_state();
else if(res[0]=="a" || res[0]=="all")
{
global_state();
local_state();
upval_state();
}
else if(res[0]=="n" || res[0]=="next")
{
next_step=true;
return;
}
else if(res[0]=="q" || res[0]=="exit")
std::exit(0);
else
err();
}
else if(res.size()==3)
{
if(res[0]=="bk" || res[0]=="break")
{
bk_fidx=get_fileindex(res[1]);
if(bk_fidx==65535)
{
printf("cannot find file named \"%s\"\n",res[1].c_str());
bk_fidx=0;
}
int tmp=atoi(res[2].c_str());
if(tmp<=0)
printf("incorrect line number \"%s\"\n",res[2].c_str());
else
bk_line=tmp;
}
else
err();
}
else
err();
}
}
void nasal_dbg::run(
const nasal_codegen& gen,
const nasal_import& linker)
{
detail_info=true;
init(gen.get_strs(),gen.get_nums(),linker.get_file());
const void* opr_table[]=
{
&&nop, &&intg, &&intl, &&loadg,
&&loadl, &&loadu, &&pnum, &&pnil,
&&pstr, &&newv, &&newh, &&newf,
&&happ, &&para, &&defpara,&&dynpara,
&&unot, &&usub, &&add, &&sub,
&&mul, &&div, &&lnk, &&addc,
&&subc, &&mulc, &&divc, &&lnkc,
&&addeq, &&subeq, &&muleq, &&diveq,
&&lnkeq, &&addeqc, &&subeqc, &&muleqc,
&&diveqc, &&lnkeqc, &&meq, &&eq,
&&neq, &&less, &&leq, &&grt,
&&geq, &&lessc, &&leqc, &&grtc,
&&geqc, &&pop, &&jmp, &&jt,
&&jf, &&counter, &&findex, &&feach,
&&callg, &&calll, &&upval, &&callv,
&&callvi, &&callh, &&callfv, &&callfh,
&&callb, &&slcbegin, &&slcend, &&slc,
&&slc2, &&mcallg, &&mcalll, &&mupval,
&&mcallv, &&mcallh, &&ret, &&vmexit
};
bytecode=gen.get_code().data();
std::vector<const void*> code;
for(auto& i:gen.get_code())
{
code.push_back(opr_table[i.op]);
imm.push_back(i.num);
}
// set canary and program counter
auto canary=gc.stack+STACK_MAX_DEPTH-1;
pc=0;
// goto the first operand
goto *code[pc];
vmexit:
if(gc.top>=canary)
die("stack overflow");
gc.clear();
imm.clear();
printf("[debug] debugger exited\n");
return;
#define dbg(op) {interact();op();if(gc.top<canary)goto *code[++pc];goto vmexit;}
nop: dbg(opr_nop );
intg: dbg(opr_intg );
intl: dbg(opr_intl );
loadg: dbg(opr_loadg );
loadl: dbg(opr_loadl );
loadu: dbg(opr_loadu );
pnum: dbg(opr_pnum );
pnil: dbg(opr_pnil );
pstr: dbg(opr_pstr );
newv: dbg(opr_newv );
newh: dbg(opr_newh );
newf: dbg(opr_newf );
happ: dbg(opr_happ );
para: dbg(opr_para );
defpara: dbg(opr_defpara );
dynpara: dbg(opr_dynpara );
unot: dbg(opr_unot );
usub: dbg(opr_usub );
add: dbg(opr_add );
sub: dbg(opr_sub );
mul: dbg(opr_mul );
div: dbg(opr_div );
lnk: dbg(opr_lnk );
addc: dbg(opr_addc );
subc: dbg(opr_subc );
mulc: dbg(opr_mulc );
divc: dbg(opr_divc );
lnkc: dbg(opr_lnkc );
addeq: dbg(opr_addeq );
subeq: dbg(opr_subeq );
muleq: dbg(opr_muleq );
diveq: dbg(opr_diveq );
lnkeq: dbg(opr_lnkeq );
addeqc: dbg(opr_addeqc );
subeqc: dbg(opr_subeqc );
muleqc: dbg(opr_muleqc );
diveqc: dbg(opr_diveqc );
lnkeqc: dbg(opr_lnkeqc );
meq: dbg(opr_meq );
eq: dbg(opr_eq );
neq: dbg(opr_neq );
less: dbg(opr_less );
leq: dbg(opr_leq );
grt: dbg(opr_grt );
geq: dbg(opr_geq );
lessc: dbg(opr_lessc );
leqc: dbg(opr_leqc );
grtc: dbg(opr_grtc );
geqc: dbg(opr_geqc );
pop: dbg(opr_pop );
jmp: dbg(opr_jmp );
jt: dbg(opr_jt );
jf: dbg(opr_jf );
counter: dbg(opr_counter );
findex: dbg(opr_findex );
feach: dbg(opr_feach );
callg: dbg(opr_callg );
calll: dbg(opr_calll );
upval: dbg(opr_upval );
callv: dbg(opr_callv );
callvi: dbg(opr_callvi );
callh: dbg(opr_callh );
callfv: dbg(opr_callfv );
callfh: dbg(opr_callfh );
callb: dbg(opr_callb );
slcbegin:dbg(opr_slcbegin);
slcend: dbg(opr_slcend );
slc: dbg(opr_slc );
slc2: dbg(opr_slc2 );
mcallg: dbg(opr_mcallg );
mcalll: dbg(opr_mcalll );
mupval: dbg(opr_mupval );
mcallv: dbg(opr_mcallv );
mcallh: dbg(opr_mcallh );
ret: dbg(opr_ret );
}
#endif

77
nasal_err.h Normal file
View File

@@ -0,0 +1,77 @@
#ifndef __NASAL_ERR_H__
#define __NASAL_ERR_H__
#include <iostream>
#include <fstream>
#include <cstring>
class file_line
{
protected:
std::string file;
std::vector<std::string> res;
public:
void load(const std::string& f)
{
if(file==f) // don't need to load a loaded file
return;
file=f;
res.clear();
std::ifstream fin(f,std::ios::binary);
if(fin.fail())
{
std::cerr<<"[src] cannot open file <"<<f<<">\n";
std::exit(1);
}
std::string line;
while(!fin.eof())
{
std::getline(fin,line);
res.push_back(line);
}
}
void clear()
{
std::vector<std::string> tmp;
res.swap(tmp);
}
const std::string& operator[](const uint32_t line){return res[line];}
const std::string& name(){return file;}
size_t size(){return res.size();}
};
class nasal_err:public file_line
{
private:
uint32_t error;
public:
void err(const char* stage,const std::string& info)
{
++error;
std::cerr<<"["<<stage<<"] "<<info<<'\n';
}
void err(const char* stage,uint32_t line,uint32_t column,const std::string& info)
{
++error;
if(!line)
{
std::cerr<<"["<<stage<<"] "<<file<<": "<<info<<'\n';
return;
}
std::cerr<<"["<<stage<<"] "<<file<<":"<<line<<":"<<column<<" "<<info<<"\n"<<res[line-1]<<'\n';
for(int i=0;i<(int)column-1;++i)
std::cerr<<char(" \t"[res[line-1][i]=='\t']);
std::cerr<<"^\n";
}
void err(const char* stage,uint32_t line,const std::string& info)
{
++error;
if(!line)
std::cerr<<"["<<stage<<"] "<<file<<": "<<info<<'\n';
else
std::cerr<<"["<<stage<<"] "<<file<<":"<<line<<" "<<info<<"\n"<<res[line-1]<<'\n';
}
void chkerr(){if(error)std::exit(1);}
};
#endif

View File

@@ -3,8 +3,13 @@
enum nasal_type
{
vm_nil=0,
/* none-gc object */
vm_none=0,
vm_cnt,
vm_ret,
vm_nil,
vm_num,
/* gc object */
vm_str,
vm_func,
vm_vec,
@@ -14,172 +19,196 @@ enum nasal_type
};
// change parameters here to make your own efficient gc
// better set bigger number on vm_num and vm_vec
const int increment[vm_type_size]=
// better set bigger number on vm_vec
const uint32_t increment[vm_type_size]=
{
0, // vm_nil,in fact it is not in use
65536,// vm_num
2048, // vm_str
512, // vm_func
8192, // vm_vec
512, // vm_hash
0 // vm_obj
/* none-gc object */
0, // vm_none, error type
0, // vm_count, used in foreach/forindex
0, // vm_ret, used to store call-return address
0, // vm_nil
0, // vm_num
/* gc object */
256, // vm_str
512, // vm_func
512, // vm_vec
128, // vm_hash
16 // vm_obj
};
// declaration of nasal_val
struct nasal_val;
// define nasal_ref => nasal_val*
typedef nasal_val* nasal_ref;
struct nasal_vec; // vector
struct nasal_hash;// hashmap(dict)
struct nasal_func;// function(lambda)
struct nasal_obj; // special objects
struct nasal_val; // nasal_val includes gc-managed types
#ifdef __NASAL_REF__
struct nasal_ref
{
uint8_t type;
union
{
double num;
uint32_t ret;
int64_t cnt;
double num;
nasal_val* gcobj;
}value;
};
#endif
struct nasal_vec// 24 bytes
nasal_ref(const uint8_t t=vm_none):type(t){}
nasal_ref(const uint8_t t,const uint32_t n):type(t){value.ret=n;}
nasal_ref(const uint8_t t,const int64_t n):type(t){value.cnt=n;}
nasal_ref(const uint8_t t,const double n):type(t){value.num=n;}
nasal_ref(const uint8_t t,nasal_val* n):type(t){value.gcobj=n;}
nasal_ref(const nasal_ref& nr):type(nr.type),value(nr.value){}
nasal_ref& operator=(const nasal_ref& nr)
{
type=nr.type;
value=nr.value;
return *this;
}
bool operator==(const nasal_ref& nr){return type==nr.type && value.gcobj==nr.value.gcobj;}
bool operator!=(const nasal_ref& nr){return type!=nr.type || value.gcobj!=nr.value.gcobj;}
// number and string can be translated to each other
double to_number();
std::string to_string();
void print();
inline uint32_t ret ();
inline int64_t& cnt ();
inline double& num ();
inline std::string* str ();
inline nasal_vec* vec ();
inline nasal_hash* hash();
inline nasal_func* func();
inline nasal_obj* obj ();
};
struct nasal_vec
{
std::vector<nasal_ref> elems;
void print();
nasal_ref get_val(int);
nasal_ref* get_mem(int);
nasal_ref get_val(const int);
nasal_ref* get_mem(const int);
};
struct nasal_hash// 56 bytes
struct nasal_hash
{
std::unordered_map<std::string,nasal_ref> elems;
void print();
nasal_ref get_val(std::string&);
nasal_ref* get_mem(std::string&);
nasal_ref get_val(const std::string&);
nasal_ref* get_mem(const std::string&);
};
struct nasal_func// 120 bytes
struct nasal_func
{
int32_t dynpara; // dynamic parameter name index in hash
uint32_t offset; // arguments will be loaded into local scope from this offset
uint32_t entry; // pc will set to entry-1 to call this function
std::vector<nasal_ref> default_para;// default value(nasal_ref)
std::unordered_map<std::string,int> key_table;// parameter name hash
nasal_ref closure; // closure will be loaded to gc.local.back() as the local scope
int32_t dynpara; // dynamic parameter name index in hash.
uint32_t entry; // pc will set to entry-1 to call this function
uint32_t psize; // used to load default parameters to a new function
uint32_t lsize; // used to expand memory space for local values on stack
std::vector<nasal_ref> local; // local scope with default value(nasal_ref)
std::vector<nasal_ref> upvalue; // closure
std::unordered_map<std::string,size_t> keys; // parameter name table, size_t begins from 1
nasal_func();
nasal_func():dynpara(-1){}
void clear();
};
struct nasal_val// 16 bytes
struct nasal_obj
{
uint32_t type;
void* ptr;
nasal_obj():ptr(nullptr){}
void clear(){ptr=nullptr;}
};
const uint8_t GC_UNCOLLECTED=0;
const uint8_t GC_COLLECTED =1;
const uint8_t GC_FOUND =2;
struct nasal_val
{
#define GC_UNCOLLECTED 0
#define GC_COLLECTED 1
#define GC_FOUND 2
uint8_t mark;
uint8_t type;
uint8_t unmut; // used to mark if a string is unmutable
union
{
double num;
std::string* str;
nasal_vec* vec;
nasal_hash* hash;
nasal_func* func;
void* obj;
nasal_obj* obj;
}ptr;
nasal_val(int);
nasal_val(uint8_t);
~nasal_val();
double to_number();
std::string to_string();
};
/*functions of nasal_vec*/
nasal_ref nasal_vec::get_val(int index)
nasal_ref nasal_vec::get_val(const int index)
{
int vec_size=elems.size();
if(index<-vec_size || index>=vec_size)
return nullptr;
return elems[index>=0?index:index+vec_size];
int size=elems.size();
if(index<-size || index>=size)
return {vm_none};
return elems[index>=0?index:index+size];
}
nasal_ref* nasal_vec::get_mem(int index)
nasal_ref* nasal_vec::get_mem(const int index)
{
int vec_size=elems.size();
if(index<-vec_size || index>=vec_size)
int size=elems.size();
if(index<-size || index>=size)
return nullptr;
return &elems[index>=0?index:index+vec_size];
return &elems[index>=0?index:index+size];
}
void nasal_vec::print()
{
static int depth=0;
if(++depth>32)
if(!elems.size() || depth>8)
{
std::cout<<"[..]";
--depth;
std::cout<<(elems.size()?"[..]":"[]");
return;
}
if(!elems.size())
{
std::cout<<"[]";
return;
}
ssize_t iter=0;
++depth;
size_t iter=0;
std::cout<<'[';
for(auto i:elems)
for(auto& i:elems)
{
switch(i->type)
{
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<i->ptr.num; break;
case vm_str: std::cout<<*i->ptr.str; break;
case vm_vec: i->ptr.vec->print(); break;
case vm_hash: i->ptr.hash->print(); break;
case vm_func: std::cout<<"func(..){..}"; break;
}
i.print();
std::cout<<",]"[(++iter)==elems.size()];
}
--depth;
return;
}
/*functions of nasal_hash*/
nasal_ref nasal_hash::get_val(std::string& key)
nasal_ref nasal_hash::get_val(const std::string& key)
{
if(elems.count(key))
return elems[key];
else if(elems.count("parents"))
{
nasal_ref ret_addr=nullptr;
nasal_ref val_addr=elems["parents"];
if(val_addr->type==vm_vec)
for(auto i:val_addr->ptr.vec->elems)
nasal_ref ret(vm_none);
nasal_ref val=elems["parents"];
if(val.type==vm_vec)
for(auto& i:val.vec()->elems)
{
if(i->type==vm_hash)
ret_addr=i->ptr.hash->get_val(key);
if(ret_addr)
return ret_addr;
if(i.type==vm_hash)
ret=i.hash()->get_val(key);
if(ret.type!=vm_none)
return ret;
}
}
return nullptr;
return {vm_none};
}
nasal_ref* nasal_hash::get_mem(std::string& key)
nasal_ref* nasal_hash::get_mem(const std::string& key)
{
if(elems.count(key))
return &elems[key];
else if(elems.count("parents"))
{
nasal_ref* mem_addr=nullptr;
nasal_ref val_addr=elems["parents"];
if(val_addr->type==vm_vec)
for(auto i:val_addr->ptr.vec->elems)
nasal_ref* addr=nullptr;
nasal_ref val=elems["parents"];
if(val.type==vm_vec)
for(auto& i:val.vec()->elems)
{
if(i->type==vm_hash)
mem_addr=i->ptr.hash->get_mem(key);
if(mem_addr)
return mem_addr;
if(i.type==vm_hash)
addr=i.hash()->get_mem(key);
if(addr)
return addr;
}
}
return nullptr;
@@ -187,66 +216,44 @@ nasal_ref* nasal_hash::get_mem(std::string& key)
void nasal_hash::print()
{
static int depth=0;
if(++depth>32)
if(!elems.size() || depth>8)
{
std::cout<<"{..}";
--depth;
return;
}
if(!elems.size())
{
std::cout<<"{}";
std::cout<<(elems.size()?"{..}":"{}");
return;
}
++depth;
size_t iter=0;
std::cout<<'{';
for(auto& i:elems)
{
std::cout<<i.first<<':';
nasal_ref tmp=i.second;
switch(tmp->type)
{
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<tmp->ptr.num; break;
case vm_str: std::cout<<*tmp->ptr.str; break;
case vm_vec: tmp->ptr.vec->print(); break;
case vm_hash: tmp->ptr.hash->print(); break;
case vm_func: std::cout<<"func(..){..}"; break;
}
i.second.print();
std::cout<<",}"[(++iter)==elems.size()];
}
--depth;
return;
}
/*functions of nasal_func*/
nasal_func::nasal_func()
{
dynpara=-1;
return;
}
void nasal_func::clear()
{
dynpara=-1;
default_para.clear();
key_table.clear();
return;
local.clear();
upvalue.clear();
keys.clear();
}
/*functions of nasal_val*/
nasal_val::nasal_val(int val_type)
nasal_val::nasal_val(uint8_t val_type)
{
mark=GC_COLLECTED;
type=val_type;
switch(type)
unmut=0;
switch(val_type)
{
case vm_num: ptr.num=0; break;
case vm_str: ptr.str=new std::string; break;
case vm_vec: ptr.vec=new nasal_vec; break;
case vm_hash: ptr.hash=new nasal_hash; break;
case vm_func: ptr.func=new nasal_func; break;
case vm_obj: ptr.obj=new nasal_obj; break;
}
return;
}
nasal_val::~nasal_val()
{
@@ -256,79 +263,100 @@ nasal_val::~nasal_val()
case vm_vec: delete ptr.vec; break;
case vm_hash: delete ptr.hash; break;
case vm_func: delete ptr.func; break;
case vm_obj: delete ptr.obj; break;
}
type=vm_nil;
return;
}
double nasal_val::to_number()
double nasal_ref::to_number()
{
return type!=vm_str?value.num:str2num(str()->c_str());
}
std::string nasal_ref::to_string()
{
if(type==vm_str)
return str2num(ptr.str->c_str());
return ptr.num;
}
std::string nasal_val::to_string()
{
if(type==vm_str)
return *ptr.str;
return *str();
else if(type==vm_num)
return std::to_string(ptr.num);
return std::to_string(num());
return "";
}
void nasal_ref::print()
{
switch(type)
{
case vm_none: std::cout<<"undefined"; break;
case vm_nil: std::cout<<"nil"; break;
case vm_num: std::cout<<value.num; break;
case vm_str: std::cout<<*(this->str());break;
case vm_vec: this->vec()->print(); break;
case vm_hash: this->hash()->print(); break;
case vm_func: std::cout<<"func(..){..}";break;
case vm_obj: std::cout<<"<object>"; break;
}
}
inline uint32_t nasal_ref::ret (){return value.ret; }
inline int64_t& nasal_ref::cnt (){return value.cnt; }
inline double& nasal_ref::num (){return value.num; }
inline std::string* nasal_ref::str (){return value.gcobj->ptr.str; }
inline nasal_vec* nasal_ref::vec (){return value.gcobj->ptr.vec; }
inline nasal_hash* nasal_ref::hash(){return value.gcobj->ptr.hash;}
inline nasal_func* nasal_ref::func(){return value.gcobj->ptr.func;}
inline nasal_obj* nasal_ref::obj (){return value.gcobj->ptr.obj; }
const uint32_t STACK_MAX_DEPTH=4095;
const nasal_ref zero={vm_num,(double)0};
const nasal_ref one ={vm_num,(double)1};
const nasal_ref nil ={vm_nil,nullptr};
struct nasal_gc
{
#define STACK_MAX_DEPTH (4095)
nasal_ref zero_addr; // reserved address of nasal_val,type vm_num, 0
nasal_ref one_addr; // reserved address of nasal_val,type vm_num, 1
nasal_ref nil_addr; // reserved address of nasal_val,type vm_nil
nasal_ref val_stack[STACK_MAX_DEPTH+1];// 1 reserved to avoid stack overflow, stack grows 1 each time
nasal_ref* stack_top; // stack top
std::vector<nasal_ref> num_addrs; // reserved address for const vm_num
std::vector<nasal_ref> str_addrs; // reserved address for const vm_str
std::vector<nasal_ref> memory; // gc memory
std::queue <nasal_ref> free_list[vm_type_size]; // gc free list
std::vector<nasal_ref> local;
void mark();
void sweep();
void gc_init(const std::vector<double>&,const std::vector<std::string>&);
void gc_clear();
nasal_ref gc_alloc(int);
nasal_ref builtin_alloc(int);
nasal_ref stack[STACK_MAX_DEPTH+1];// 1 reserved to avoid stack overflow
nasal_ref* top; // stack top
std::vector<nasal_ref> strs; // reserved address for const vm_str
std::vector<nasal_val*> memory; // gc memory
std::queue<nasal_val*> free_list[vm_type_size]; // gc free list
std::vector<nasal_ref> local;
uint64_t size[vm_type_size];
uint64_t count[vm_type_size];
void mark();
void sweep();
void init(const std::vector<std::string>&);
void clear();
void info();
nasal_ref alloc(const uint8_t);
nasal_ref builtin_alloc(const uint8_t);
};
/* gc functions */
void nasal_gc::mark()
{
std::queue<nasal_ref> bfs;
for(auto i:local)
for(auto& i:local)
bfs.push(i);
for(nasal_ref* i=val_stack;i<=stack_top;++i)
for(nasal_ref* i=stack;i<=top;++i)
bfs.push(*i);
while(!bfs.empty())
{
nasal_ref tmp=bfs.front();
bfs.pop();
if(tmp->mark) continue;
tmp->mark=GC_FOUND;
switch(tmp->type)
if(tmp.type<=vm_num || tmp.value.gcobj->mark) continue;
tmp.value.gcobj->mark=GC_FOUND;
switch(tmp.type)
{
case vm_vec:
for(auto i:tmp->ptr.vec->elems)
for(auto& i:tmp.vec()->elems)
bfs.push(i);
break;
case vm_hash:
for(auto& i:tmp->ptr.hash->elems)
for(auto& i:tmp.hash()->elems)
bfs.push(i.second);
break;
case vm_func:
bfs.push(tmp->ptr.func->closure);
for(auto i:tmp->ptr.func->default_para)
if(i)
bfs.push(i);
for(auto& i:tmp.func()->local)
bfs.push(i);
for(auto& i:tmp.func()->upvalue)
bfs.push(i);
break;
}
}
return;
}
void nasal_gc::sweep()
{
@@ -342,6 +370,7 @@ void nasal_gc::sweep()
case vm_vec: i->ptr.vec->elems.clear(); break;
case vm_hash:i->ptr.hash->elems.clear();break;
case vm_func:i->ptr.func->clear(); break;
case vm_obj: i->ptr.obj->clear(); break;
}
free_list[i->type].push(i);
i->mark=GC_COLLECTED;
@@ -349,99 +378,96 @@ void nasal_gc::sweep()
else if(i->mark==GC_FOUND)
i->mark=GC_UNCOLLECTED;
}
return;
}
void nasal_gc::gc_init(const std::vector<double>& nums,const std::vector<std::string>& strs)
void nasal_gc::init(const std::vector<std::string>& s)
{
for(int i=vm_num;i<vm_type_size;++i)
for(int j=0;j<increment[i];++j)
for(uint8_t i=0;i<vm_type_size;++i)
size[i]=count[i]=0;
for(uint8_t i=vm_str;i<vm_type_size;++i)
for(uint32_t j=0;j<increment[i];++j)
{
nasal_ref tmp=new nasal_val(i);
nasal_val* tmp=new nasal_val(i);
memory.push_back(tmp);
free_list[i].push(tmp);
}
stack_top=val_stack; // set stack_top to val_stack
zero_addr=new nasal_val(vm_num); // init constant 0
zero_addr->ptr.num=0;
one_addr=new nasal_val(vm_num); // init constant 1
one_addr->ptr.num=1;
nil_addr=new nasal_val(vm_nil); // init nil
// init constant numbers
num_addrs.resize(nums.size());
for(int i=0;i<nums.size();++i)
{
num_addrs[i]=new nasal_val(vm_num);
num_addrs[i]->ptr.num=nums[i];
}
top=stack;
// init constant strings
str_addrs.resize(strs.size());
for(int i=0;i<strs.size();++i)
strs.resize(s.size());
for(uint32_t i=0;i<strs.size();++i)
{
str_addrs[i]=new nasal_val(vm_str);
*str_addrs[i]->ptr.str=strs[i];
strs[i]={vm_str,new nasal_val(vm_str)};
strs[i].value.gcobj->unmut=1;
*strs[i].str()=s[i];
}
return;
}
void nasal_gc::gc_clear()
void nasal_gc::clear()
{
for(auto i:memory)
delete i;
memory.clear();
for(int i=0;i<vm_type_size;++i)
for(uint8_t i=0;i<vm_type_size;++i)
while(!free_list[i].empty())
free_list[i].pop();
local.clear();
delete nil_addr;
delete one_addr;
delete zero_addr;
for(auto i:num_addrs)
delete i;
num_addrs.clear();
for(auto i:str_addrs)
delete i;
str_addrs.clear();
return;
for(auto& i:strs)
delete i.value.gcobj;
strs.clear();
}
nasal_ref nasal_gc::gc_alloc(int type)
void nasal_gc::info()
{
const char* name[]={
"null","cnt ","ret ",
"nil ","num ","str ",
"func","vec ","hash","obj "
};
std::cout<<"\ngarbage collector info:\n";
for(uint8_t i=vm_str;i<vm_type_size;++i)
std::cout<<name[i]<<" | "<<count[i]<<"\n";
std::cout<<"\nmemory allocator info(max size):\n";
for(uint8_t i=vm_str;i<vm_type_size;++i)
std::cout<<name[i]<<" | "<<(size[i]+1)*increment[i]<<"(+"<<size[i]<<")\n";
}
nasal_ref nasal_gc::alloc(uint8_t type)
{
if(free_list[type].empty())
{
++count[type];
mark();
sweep();
}
if(free_list[type].empty())
for(int i=0;i<increment[type];++i)
{
++size[type];
for(uint32_t i=0;i<increment[type];++i)
{
nasal_ref tmp=new nasal_val(type);
nasal_val* tmp=new nasal_val(type);
memory.push_back(tmp);
free_list[type].push(tmp);
}
nasal_ref ret=free_list[type].front();
ret->mark=GC_UNCOLLECTED;
}
nasal_ref ret={type,free_list[type].front()};
ret.value.gcobj->mark=GC_UNCOLLECTED;
free_list[type].pop();
return ret;
}
nasal_ref nasal_gc::builtin_alloc(int type)
nasal_ref nasal_gc::builtin_alloc(uint8_t type)
{
// when running a builtin function,alloc will run more than one time
// this may cause mark-sweep in gc_alloc
// this may cause mark-sweep in gc::alloc
// and the value got before will be collected,this is a fatal error
// so use builtin_alloc in builtin functions if this function uses alloc more then one time
if(free_list[type].empty())
for(int i=0;i<increment[type];++i)
{
++size[type];
for(uint32_t i=0;i<increment[type];++i)
{
nasal_ref tmp=new nasal_val(type);
nasal_val* tmp=new nasal_val(type);
memory.push_back(tmp);
free_list[type].push(tmp);
}
nasal_ref ret=free_list[type].front();
ret->mark=GC_UNCOLLECTED;
}
nasal_ref ret={type,free_list[type].front()};
ret.value.gcobj->mark=GC_UNCOLLECTED;
free_list[type].pop();
return ret;
}

View File

@@ -1,134 +1,99 @@
#ifndef __NASAL_IMPORT_H__
#define __NASAL_IMPORT_H__
class nasal_import
{
private:
int error;
nasal_lexer import_lex;
nasal_parse import_par;
nasal_ast import_ast;
std::vector<std::string> filename_table;
void die(const std::string&,const char*);
bool check_import(const nasal_ast&);
bool check_exist(const std::string&);
void linker(nasal_ast&,nasal_ast&&);
nasal_ast file_import(nasal_ast&);
nasal_ast load(nasal_ast&,uint16_t);
public:
const int get_error(){return error;}
void link(nasal_ast&,const std::string&);
const nasal_ast&
get_root(){return import_ast;}
const std::vector<std::string>&
get_file(){return filename_table;}
};
void nasal_import::die(const std::string& filename,const char* error_stage)
{
++error;
std::cout<<"[import] in <\""<<filename<<"\">: error(s) occurred in "<<error_stage<<".\n";
return;
}
bool nasal_import::check_import(const nasal_ast& node)
{
/*
only this kind of node can be recognized as 'import':
call
id:import
call_func
string:'filename'
*/
if(node.get_type()!=ast_call)
return false;
const std::vector<nasal_ast>& ref_vec=node.get_children();
if(ref_vec.size()!=2)
return false;
if(ref_vec[0].get_str()!="import")
return false;
if(ref_vec[1].get_type()!=ast_callf)
return false;
if(ref_vec[1].get_children().size()!=1 || ref_vec[1].get_children()[0].get_type()!=ast_str)
return false;
return true;
}
bool nasal_import::check_exist(const std::string& file)
{
// avoid importing the same file
for(auto& fname:filename_table)
if(file==fname)
return true;
filename_table.push_back(file);
return false;
}
void nasal_import::linker(nasal_ast& root,nasal_ast&& add_root)
{
// add children of add_root to the back of root
for(auto& i:add_root.get_children())
root.add_child(std::move(i));
return;
}
nasal_ast nasal_import::file_import(nasal_ast& node)
{
// initializing
nasal_ast tmp(0,ast_root);
// get filename and set node to ast_null
std::string filename=node.get_children()[1].get_children()[0].get_str();
node.clear();
// avoid infinite loading loop
if(check_exist(filename))
return tmp;
// start importing...
import_lex.openfile(filename);
import_lex.scanner();
if(import_lex.get_error())
{
die(filename,"lexer");
return tmp;
}
import_par.main_process(import_lex.get_token_list());
if(import_par.get_error())
{
die(filename,"parser");
return tmp;
}
tmp=std::move(import_par.get_root());
// check if tmp has 'import'
return load(tmp,filename_table.size()-1);
}
nasal_ast nasal_import::load(nasal_ast& root,uint16_t fileindex)
{
nasal_ast new_root(0,ast_root);
for(auto& i:root.get_children())
if(check_import(i))
linker(new_root,file_import(i));
// add root to the back of new_root
nasal_ast file_head(0,ast_file);
file_head.set_num(fileindex);
new_root.add_child(std::move(file_head));
linker(new_root,std::move(root));
return new_root;
}
void nasal_import::link(nasal_ast& root,const std::string& self)
{
// initializing
error=0;
filename_table.clear();
filename_table.push_back(self);
import_ast.clear();
// scan root and import files,then generate a new ast and return to import_ast
// the main file's index is 0
import_ast=load(root,0);
return;
}
#ifndef __NASAL_IMPORT_H__
#define __NASAL_IMPORT_H__
class nasal_import
{
private:
nasal_err& nerr;
std::vector<std::string> files;
bool check_import(const nasal_ast&);
bool check_exist(const std::string&);
void linker(nasal_ast&,nasal_ast&&);
nasal_ast file_import(nasal_ast&);
nasal_ast load(nasal_ast&,uint16_t);
public:
nasal_import(nasal_err& e):nerr(e){}
void link(nasal_parse&,const std::string&);
const std::vector<std::string>& get_file() const {return files;}
};
bool nasal_import::check_import(const nasal_ast& node)
{
/*
only this kind of node can be recognized as 'import':
call
|_id:import
|_call_func
|_string:'filename'
*/
return (
node.type()==ast_call &&
node.size()==2 &&
node[0].str()=="import" &&
node[1].type()==ast_callf &&
node[1].size()==1 &&
node[1][0].type()==ast_str
);
}
bool nasal_import::check_exist(const std::string& file)
{
// avoid importing the same file
for(auto& fname:files)
if(file==fname)
return true;
files.push_back(file);
return false;
}
void nasal_import::linker(nasal_ast& root,nasal_ast&& add_root)
{
// add children of add_root to the back of root
for(auto& i:add_root.child())
root.add(std::move(i));
}
nasal_ast nasal_import::file_import(nasal_ast& node)
{
nasal_lexer lex(nerr);
nasal_parse par(nerr);
// get filename and set node to ast_null
std::string filename=node[1][0].str();
node.clear();
// avoid infinite loading loop
if(check_exist(filename))
return {0,ast_root};
// start importing...
lex.scan(filename);
par.compile(lex);
nasal_ast tmp=std::move(par.ast());
// check if tmp has 'import'
return load(tmp,files.size()-1);
}
nasal_ast nasal_import::load(nasal_ast& root,uint16_t fileindex)
{
nasal_ast new_root(0,ast_root);
for(auto& i:root.child())
if(check_import(i))
linker(new_root,file_import(i));
// add root to the back of new_root
nasal_ast file_head(0,ast_file);
file_head.set_num(fileindex);
new_root.add(std::move(file_head));
linker(new_root,std::move(root));
return new_root;
}
void nasal_import::link(nasal_parse& parse,const std::string& self)
{
// initializing
files={self};
// scan root and import files,then generate a new ast and return to import_ast
// the main file's index is 0
parse.ast()=load(parse.ast(),0);
}
#endif

View File

@@ -1,362 +1,354 @@
#ifndef __NASAL_LEXER_H__
#define __NASAL_LEXER_H__
#define IS_IDENTIFIER(c) ((c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z'))
#define IS_HEX_NUMBER(c) (('0'<=c&&c<='9')||('a'<=c&&c<='f')||('A'<=c && c<='F'))
#define IS_OCT_NUMEBR(c) ('0'<=c&&c<='7')
#define IS_DIGIT(c) ('0'<=c&&c<='9')
#define IS_STRING(c) (c=='\''||c=='\"'||c=='`')
// single operators have only one character
#define IS_SINGLE_OPERATOR(c) (c=='('||c==')'||c=='['||c==']'||c=='{'||c=='}'||c==','||c==';'||c=='|'||c==':'||\
c=='?'||c=='`'||c=='&'||c=='@'||c=='%'||c=='$'||c=='^'||c=='\\')
// calculation operators may have two chars, for example: += -= *= /= ~= != == >= <=
#define IS_CALC_OPERATOR(c) (c=='='||c=='+'||c=='-'||c=='*'||c=='!'||c=='/'||c=='<'||c=='>'||c=='~')
#define IS_NOTE(c) (c=='#')
enum token_type
{
tok_null=0,
tok_num,tok_str,tok_id,
tok_for,tok_forindex,tok_foreach,tok_while,
tok_var,tok_func,tok_break,tok_continue,
tok_ret,tok_if,tok_elsif,tok_else,tok_nil,
tok_lcurve,tok_rcurve,
tok_lbracket,tok_rbracket,
tok_lbrace,tok_rbrace,
tok_semi,tok_and,tok_or,tok_comma,tok_dot,tok_ellipsis,tok_quesmark,
tok_colon,tok_add,tok_sub,tok_mult,tok_div,tok_link,tok_not,
tok_eq,
tok_addeq,tok_subeq,tok_multeq,tok_diveq,tok_lnkeq,
tok_cmpeq,tok_neq,tok_less,tok_leq,tok_grt,tok_geq,
tok_eof
};
struct
{
const char* str;
const int tok_type;
}token_table[]=
{
{"for" ,tok_for },
{"forindex",tok_forindex },
{"foreach" ,tok_foreach },
{"while" ,tok_while },
{"var" ,tok_var },
{"func" ,tok_func },
{"break" ,tok_break },
{"continue",tok_continue },
{"return" ,tok_ret },
{"if" ,tok_if },
{"elsif" ,tok_elsif },
{"else" ,tok_else },
{"nil" ,tok_nil },
{"(" ,tok_lcurve },
{")" ,tok_rcurve },
{"[" ,tok_lbracket },
{"]" ,tok_rbracket },
{"{" ,tok_lbrace },
{"}" ,tok_rbrace },
{";" ,tok_semi },
{"and" ,tok_and },
{"or" ,tok_or },
{"," ,tok_comma },
{"." ,tok_dot },
{"..." ,tok_ellipsis },
{"?" ,tok_quesmark },
{":" ,tok_colon },
{"+" ,tok_add },
{"-" ,tok_sub },
{"*" ,tok_mult },
{"/" ,tok_div },
{"~" ,tok_link },
{"!" ,tok_not },
{"=" ,tok_eq },
{"+=" ,tok_addeq },
{"-=" ,tok_subeq },
{"*=" ,tok_multeq },
{"/=" ,tok_diveq },
{"~=" ,tok_lnkeq },
{"==" ,tok_cmpeq },
{"!=" ,tok_neq },
{"<" ,tok_less },
{"<=" ,tok_leq },
{">" ,tok_grt },
{">=" ,tok_geq },
{nullptr ,-1 }
};
struct token
{
int line;
int type;
std::string str;
token(int l=0,int t=tok_null,std::string s=""){line=l;type=t;str=s;}
};
class nasal_lexer
{
private:
int error;
int res_size;
int line;
int ptr;
std::string line_code;
std::string res;
std::vector<token> token_list;
int get_tok_type(const std::string&);
void die(const char*);
std::string id_gen();
std::string num_gen();
std::string str_gen();
public:
void openfile(const std::string&);
void scanner();
void print_token();
int get_error(){return error;}
std::vector<token>& get_token_list(){return token_list;}
};
void nasal_lexer::openfile(const std::string& filename)
{
error=0;
res.clear();
std::ifstream fin(filename,std::ios::binary);
if(fin.fail())
{
++error;
std::cout<<"[lexer] cannot open file <"<<filename<<">.\n";
return;
}
while(!fin.eof())
{
char c=fin.get();
if(fin.eof())
break;
res+=c;
}
return;
}
int nasal_lexer::get_tok_type(const std::string& tk_str)
{
for(int i=0;token_table[i].str;++i)
if(tk_str==token_table[i].str)
return token_table[i].tok_type;
return tok_null;
}
void nasal_lexer::die(const char* error_info)
{
++error;
std::cout<<"[lexer] line "<<line<<" column "<<line_code.length()<<": \n"<<line_code<<"\n";
for(auto i:line_code)
std::cout<<(i=='\t'?'\t':' ');
std::cout<<"^"<<error_info<<'\n';
return;
}
std::string nasal_lexer::id_gen()
{
std::string token_str="";
while(ptr<res_size && (IS_IDENTIFIER(res[ptr])||IS_DIGIT(res[ptr])))
token_str+=res[ptr++];
line_code+=token_str;
return token_str;
// after running this process, ptr will point to the next token's beginning character
}
std::string nasal_lexer::num_gen()
{
// generate hex number
if(ptr+1<res_size && res[ptr]=='0' && res[ptr+1]=='x')
{
std::string token_str="0x";
ptr+=2;
while(ptr<res_size && IS_HEX_NUMBER(res[ptr]))
token_str+=res[ptr++];
line_code+=token_str;
if(token_str.length()<3)// "0x"
die("incorrect number.");
return token_str;
}
// generate oct number
else if(ptr+1<res_size && res[ptr]=='0' && res[ptr+1]=='o')
{
std::string token_str="0o";
ptr+=2;
while(ptr<res_size && IS_OCT_NUMEBR(res[ptr]))
token_str+=res[ptr++];
line_code+=token_str;
if(token_str.length()<3)// "0o"
die("incorrect number.");
return token_str;
}
// generate dec number
// dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*)
std::string token_str="";
while(ptr<res_size && IS_DIGIT(res[ptr]))
token_str+=res[ptr++];
if(ptr<res_size && res[ptr]=='.')
{
token_str+=res[ptr++];
while(ptr<res_size && IS_DIGIT(res[ptr]))
token_str+=res[ptr++];
// "xxxx." is not a correct number
if(token_str.back()=='.')
{
line_code+=token_str;
die("incorrect number.");
return "0";
}
}
if(ptr<res_size && (res[ptr]=='e' || res[ptr]=='E'))
{
token_str+=res[ptr++];
if(ptr<res_size && (res[ptr]=='-' || res[ptr]=='+'))
token_str+=res[ptr++];
while(ptr<res_size && IS_DIGIT(res[ptr]))
token_str+=res[ptr++];
// "xxxe(-|+)" is not a correct number
if(token_str.back()=='e' || token_str.back()=='E' || token_str.back()=='-' || token_str.back()=='+')
{
line_code+=token_str;
die("incorrect number.");
return "0";
}
}
line_code+=token_str;
return token_str;
}
std::string nasal_lexer::str_gen()
{
std::string token_str="";
char str_begin=res[ptr];
line_code+=str_begin;
while(++ptr<res_size && res[ptr]!=str_begin)
{
line_code+=res[ptr];
if(res[ptr]=='\n')
{
line_code="";
++line;
}
if(res[ptr]=='\\' && ptr+1<res_size)
{
line_code+=res[++ptr];
switch(res[ptr])
{
case 'a': token_str.push_back('\a');break;
case 'b': token_str.push_back('\b');break;
case 'f': token_str.push_back('\f');break;
case 'n': token_str.push_back('\n');break;
case 'r': token_str.push_back('\r');break;
case 't': token_str.push_back('\t');break;
case 'v': token_str.push_back('\v');break;
case '?': token_str.push_back('\?');break;
case '0': token_str.push_back('\0');break;
case '\\':token_str.push_back('\\');break;
case '\'':token_str.push_back('\'');break;
case '\"':token_str.push_back('\"');break;
default: token_str.push_back(res[ptr]);break;
}
continue;
}
token_str+=res[ptr];
}
// check if this string ends with a " or '
if(ptr++>=res_size)
die("get EOF when generating string.");
if(str_begin=='`' && token_str.length()!=1)
die("\'`\' is used for string that includes one character.");
return token_str;
}
void nasal_lexer::scanner()
{
token_list.clear();
line=1;
ptr=0;
line_code="";
res_size=res.size();
std::string token_str;
while(ptr<res_size)
{
while(ptr<res_size && (res[ptr]==' ' || res[ptr]=='\n' || res[ptr]=='\t' || res[ptr]=='\r' || res[ptr]<0))
{
// these characters will be ignored, and '\n' will cause ++line
line_code+=res[ptr];
if(res[ptr++]=='\n')
{
++line;
line_code="";
}
}
if(ptr>=res_size) break;
if(IS_IDENTIFIER(res[ptr]))
{
token_str=id_gen();
token_list.push_back({line,get_tok_type(token_str),token_str});
if(!token_list.back().type)
token_list.back().type=tok_id;
}
else if(IS_DIGIT(res[ptr]))
token_list.push_back({line,tok_num,num_gen()});
else if(IS_STRING(res[ptr]))
token_list.push_back({line,tok_str,str_gen()});
else if(IS_SINGLE_OPERATOR(res[ptr]))
{
token_str=res[ptr];
line_code+=res[ptr];
int type=get_tok_type(token_str);
if(!type)
die("incorrect operator.");
token_list.push_back({line,type,token_str});
++ptr;
}
else if(res[ptr]=='.')
{
if(ptr+2<res_size && res[ptr+1]=='.' && res[ptr+2]=='.')
{
token_str="...";
ptr+=3;
}
else
{
token_str=".";
++ptr;
}
line_code+=token_str;
token_list.push_back({line,get_tok_type(token_str),token_str});
}
else if(IS_CALC_OPERATOR(res[ptr]))
{
// get calculation operator
token_str=res[ptr++];
if(ptr<res_size && res[ptr]=='=')
token_str+=res[ptr++];
line_code+=token_str;
token_list.push_back({line,get_tok_type(token_str),token_str});
}
else if(IS_NOTE(res[ptr]))// avoid note, after this process ptr will point to a '\n', so next loop line counter+1
while(++ptr<res_size && res[ptr]!='\n');
else
{
line_code+=res[ptr++];
die("unknown character.");
}
}
token_list.push_back({line,tok_eof,""});
res.clear();
return;
}
void nasal_lexer::print_token()
{
for(auto& tok:token_list)
std::cout<<"("<<tok.line<<" | "<<tok.str<<")\n";
return;
}
#ifndef __NASAL_LEXER_H__
#define __NASAL_LEXER_H__
#define ID(c) ((c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z'))
#define HEX(c) (('0'<=c&&c<='9')||('a'<=c&&c<='f')||('A'<=c && c<='F'))
#define OCT(c) ('0'<=c&&c<='7')
#define DIGIT(c) ('0'<=c&&c<='9')
#define STR(c) (c=='\''||c=='\"'||c=='`')
// single operators have only one character
#define SINGLE_OPERATOR(c) (c=='('||c==')'||c=='['||c==']'||c=='{'||c=='}'||c==','||c==';'||c=='|'||c==':'||\
c=='?'||c=='`'||c=='&'||c=='@'||c=='%'||c=='$'||c=='^'||c=='\\')
// calculation operators may have two chars, for example: += -= *= /= ~= != == >= <=
#define CALC_OPERATOR(c) (c=='='||c=='+'||c=='-'||c=='*'||c=='!'||c=='/'||c=='<'||c=='>'||c=='~')
#define NOTE(c) (c=='#')
enum token_type
{
tok_null=0,// null token default token type
tok_num, // number basic token type
tok_str, // string basic token type
tok_id, // identifier basic token type
tok_for,tok_forindex,tok_foreach,tok_while,
tok_var,tok_func,tok_break,tok_continue,
tok_ret,tok_if,tok_elsif,tok_else,tok_nil,
tok_lcurve,tok_rcurve,
tok_lbracket,tok_rbracket,
tok_lbrace,tok_rbrace,
tok_semi,tok_and,tok_or,tok_comma,tok_dot,tok_ellipsis,tok_quesmark,
tok_colon,tok_add,tok_sub,tok_mult,tok_div,tok_link,tok_not,
tok_eq,
tok_addeq,tok_subeq,tok_multeq,tok_diveq,tok_lnkeq,
tok_cmpeq,tok_neq,tok_less,tok_leq,tok_grt,tok_geq,
tok_eof // end of token list
};
struct
{
const char* str;
const uint32_t tok_type;
}token_table[]=
{
{"for" ,tok_for },
{"forindex",tok_forindex },
{"foreach" ,tok_foreach },
{"while" ,tok_while },
{"var" ,tok_var },
{"func" ,tok_func },
{"break" ,tok_break },
{"continue",tok_continue },
{"return" ,tok_ret },
{"if" ,tok_if },
{"elsif" ,tok_elsif },
{"else" ,tok_else },
{"nil" ,tok_nil },
{"(" ,tok_lcurve },
{")" ,tok_rcurve },
{"[" ,tok_lbracket },
{"]" ,tok_rbracket },
{"{" ,tok_lbrace },
{"}" ,tok_rbrace },
{";" ,tok_semi },
{"and" ,tok_and },
{"or" ,tok_or },
{"," ,tok_comma },
{"." ,tok_dot },
{"..." ,tok_ellipsis },
{"?" ,tok_quesmark },
{":" ,tok_colon },
{"+" ,tok_add },
{"-" ,tok_sub },
{"*" ,tok_mult },
{"/" ,tok_div },
{"~" ,tok_link },
{"!" ,tok_not },
{"=" ,tok_eq },
{"+=" ,tok_addeq },
{"-=" ,tok_subeq },
{"*=" ,tok_multeq },
{"/=" ,tok_diveq },
{"~=" ,tok_lnkeq },
{"==" ,tok_cmpeq },
{"!=" ,tok_neq },
{"<" ,tok_less },
{"<=" ,tok_leq },
{">" ,tok_grt },
{">=" ,tok_geq },
{nullptr ,0 }
};
struct token
{
uint32_t line;
uint32_t column;
uint32_t type;
std::string str;
token(uint32_t l=0,uint32_t c=0,uint32_t t=tok_null,std::string s="")
{
line=l;
column=c;
type=t;
str=s;
}
};
class nasal_lexer
{
private:
uint32_t line;
uint32_t column;
uint32_t ptr;
nasal_err& nerr;
std::string res;
std::vector<token> tokens;
uint32_t get_type(const std::string&);
void die(std::string info){nerr.err("lexer",line,column,info);};
void open(const std::string&);
std::string id_gen();
std::string num_gen();
std::string str_gen();
public:
nasal_lexer(nasal_err& e):nerr(e){}
void scan(const std::string&);
void print();
const std::vector<token>& get_tokens() const {return tokens;}
};
void nasal_lexer::open(const std::string& file)
{
std::ifstream fin(file,std::ios::binary);
if(fin.fail())
nerr.err("lexer","cannot open file <"+file+">.");
else
nerr.load(file);
std::stringstream ss;
ss<<fin.rdbuf();
res=ss.str();
}
uint32_t nasal_lexer::get_type(const std::string& tk_str)
{
for(int i=0;token_table[i].str;++i)
if(tk_str==token_table[i].str)
return token_table[i].tok_type;
return tok_null;
}
std::string nasal_lexer::id_gen()
{
std::string str="";
while(ptr<res.size() && (ID(res[ptr])||DIGIT(res[ptr])))
str+=res[ptr++];
column+=str.length();
return str;
}
std::string nasal_lexer::num_gen()
{
// generate hex number
if(ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='x')
{
std::string str="0x";
ptr+=2;
while(ptr<res.size() && HEX(res[ptr]))
str+=res[ptr++];
column+=str.length();
if(str.length()<3)// "0x"
die("invalid number:"+str);
return str;
}
// generate oct number
else if(ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='o')
{
std::string str="0o";
ptr+=2;
while(ptr<res.size() && OCT(res[ptr]))
str+=res[ptr++];
column+=str.length();
if(str.length()<3)// "0o"
die("invalid number:"+str);
return str;
}
// generate dec number
// dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*)
std::string str="";
while(ptr<res.size() && DIGIT(res[ptr]))
str+=res[ptr++];
if(ptr<res.size() && res[ptr]=='.')
{
str+=res[ptr++];
while(ptr<res.size() && DIGIT(res[ptr]))
str+=res[ptr++];
// "xxxx." is not a correct number
if(str.back()=='.')
{
column+=str.length();
die("invalid number:"+str);
return "0";
}
}
if(ptr<res.size() && (res[ptr]=='e' || res[ptr]=='E'))
{
str+=res[ptr++];
if(ptr<res.size() && (res[ptr]=='-' || res[ptr]=='+'))
str+=res[ptr++];
while(ptr<res.size() && DIGIT(res[ptr]))
str+=res[ptr++];
// "xxxe(-|+)" is not a correct number
if(str.back()=='e' || str.back()=='E' || str.back()=='-' || str.back()=='+')
{
column+=str.length();
die("invalid number:"+str);
return "0";
}
}
column+=str.length();
return str;
}
std::string nasal_lexer::str_gen()
{
std::string str="";
char begin=res[ptr];
++column;
while(++ptr<res.size() && res[ptr]!=begin)
{
++column;
if(res[ptr]=='\n')
{
column=0;
++line;
}
if(res[ptr]=='\\' && ptr+1<res.size())
{
++column;
++ptr;
switch(res[ptr])
{
case '0': str+='\0'; break;
case 'a': str+='\a'; break;
case 'b': str+='\b'; break;
case 't': str+='\t'; break;
case 'n': str+='\n'; break;
case 'v': str+='\v'; break;
case 'f': str+='\f'; break;
case 'r': str+='\r'; break;
case '?': str+='\?'; break;
case '\\':str+='\\'; break;
case '\'':str+='\''; break;
case '\"':str+='\"'; break;
default: str+=res[ptr];break;
}
continue;
}
str+=res[ptr];
}
// check if this string ends with a " or '
if(ptr++>=res.size())
{
die("get EOF when generating string.");
return str;
}
++column;
if(begin=='`' && str.length()!=1)
die("\'`\' is used for string that includes one character.");
return str;
}
void nasal_lexer::scan(const std::string& file)
{
line=1;
column=0;
ptr=0;
open(file);
std::string str;
while(ptr<res.size())
{
while(ptr<res.size() && (res[ptr]==' ' || res[ptr]=='\n' || res[ptr]=='\t' || res[ptr]=='\r' || res[ptr]<0))
{
// these characters will be ignored, and '\n' will cause ++line
++column;
if(res[ptr++]=='\n')
{
++line;
column=0;
}
}
if(ptr>=res.size()) break;
if(ID(res[ptr]))
{
str=id_gen();
uint32_t type=get_type(str);
tokens.push_back({line,column,type?type:tok_id,str});
}
else if(DIGIT(res[ptr]))
{
str=num_gen(); // make sure column is correct
tokens.push_back({line,column,tok_num,str});
}
else if(STR(res[ptr]))
{
str=str_gen(); // make sure column is correct
tokens.push_back({line,column,tok_str,str});
}
else if(SINGLE_OPERATOR(res[ptr]))
{
str=res[ptr];
++column;
uint32_t type=get_type(str);
if(!type)
die("invalid operator:"+str);
tokens.push_back({line,column,type,str});
++ptr;
}
else if(res[ptr]=='.')
{
str=".";
if(ptr+2<res.size() && res[ptr+1]=='.' && res[ptr+2]=='.')
str+="..";
ptr+=str.length();
column+=str.length();
tokens.push_back({line,column,get_type(str),str});
}
else if(CALC_OPERATOR(res[ptr]))
{
// get calculation operator
str=res[ptr++];
if(ptr<res.size() && res[ptr]=='=')
str+=res[ptr++];
column+=str.length();
tokens.push_back({line,column,get_type(str),str});
}
else if(NOTE(res[ptr]))// avoid note, after this process ptr will point to a '\n', so next loop line counter+1
while(++ptr<res.size() && res[ptr]!='\n');
else
{
++column;
++ptr;
die("unknown character.");
}
}
tokens.push_back({line,column,tok_eof,"eof"});
res="";
nerr.chkerr();
}
void nasal_lexer::print()
{
for(auto& tok:tokens)
std::cout<<"("<<tok.line<<" | "<<rawstr(tok.str)<<")\n";
}
#endif

62
nasal_opt.h Normal file
View File

@@ -0,0 +1,62 @@
#ifndef __NASAL_OPT_H__
#define __NASAL_OPT_H__
void const_str(nasal_ast& root)
{
auto& vec=root.child();
root.set_str(vec[0].str()+vec[1].str());
root.child().clear();
root.set_type(ast_str);
}
void const_num(nasal_ast& root)
{
auto& vec=root.child();
double res;
switch(root.type())
{
case ast_add: res=vec[0].num()+vec[1].num(); break;
case ast_sub: res=vec[0].num()-vec[1].num(); break;
case ast_mult:res=vec[0].num()*vec[1].num(); break;
case ast_div: res=vec[0].num()/vec[1].num(); break;
case ast_less:res=vec[0].num()<vec[1].num(); break;
case ast_leq: res=vec[0].num()<=vec[1].num();break;
case ast_grt: res=vec[0].num()>vec[1].num(); break;
case ast_geq: res=vec[0].num()>=vec[1].num();break;
}
// inf and nan will cause number hashmap error in codegen
if(std::isinf(res) || std::isnan(res))
return;
root.set_num(res);
root.child().clear();
root.set_type(ast_num);
}
void calc_const(nasal_ast& root)
{
auto& vec=root.child();
for(auto& i:vec)
calc_const(i);
if(vec.size()!=2)
return;
if(root.type()!=ast_add &&
root.type()!=ast_sub &&
root.type()!=ast_mult &&
root.type()!=ast_div &&
root.type()!=ast_link &&
root.type()!=ast_less &&
root.type()!=ast_leq &&
root.type()!=ast_grt &&
root.type()!=ast_geq)
return;
if(root.type()==ast_link && vec[0].type()==ast_str && vec[1].type()==ast_str)
const_str(root);
else if(root.type()!=ast_link && vec[0].type()==ast_num && vec[1].type()==ast_num)
const_num(root);
}
void optimize(nasal_ast& root)
{
for(auto& i:root.child())
calc_const(i);
}
#endif

File diff suppressed because it is too large Load Diff

1181
nasal_vm.h

File diff suppressed because it is too large Load Diff

BIN
pic/benchmark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -1,149 +1,318 @@
var import=func(filename)
{
# lib.nas
# import is used to link another file, this lib function will do nothing.
# because nasal_import will recognize this and link files before generating bytecode.
var import=func(filename){
return __builtin_import(filename);
}
var print=func(elems...)
{
# print is used to print all things in nasal, try and see how it works.
# this function uses std::cout/printf to output logs.
var print=func(elems...){
return __builtin_print(elems);
};
var println=func(elems...)
{
}
# append is used to add values into a vector.
var append=func(vec,elems...){
return __builtin_append(vec,elems);
}
# setsize is used to change the size of vector.
# if the size is larger than before,
# this function will fill vm_nil into uninitialized space.
var setsize=func(vec,size){
return __builtin_setsize(vec,size);
}
# system has the same use in C.
var system=func(str){
return __builtin_system(str);
}
# input uses std::cin and returns what we input.
var input=func(){
return __builtin_input();
}
# split a string by delimiter for example:
# split("ll","hello world") -> ["he","o world"]
# this function will return a vector.
var split=func(deli,str){
return __builtin_split(deli,str);
}
# rand has the same function as the rand in C
# if seed is nil, it will return the random number.
# if seed is not nil, it will be initialized by this seed.
var rand=func(seed=nil){
return __builtin_rand(seed);
}
# id will return the pointer of an gc-object.
# if this object is not managed by gc, it will return 0.
var id=func(object){
return __builtin_id(object);
}
# int will get the integer of input number.
# but carefully use it, because int has range between -2147483648~2147483647
var int=func(val){
return __builtin_int(val);
}
# num will change all the other types into number.
# mostly used to change a numerable string.
var num=func(val){
return __builtin_num(val);
}
# pop used to pop the last element in a vector.
# this function will return the value that poped if vector has element(s).
# if the vector is empty, it will return nil.
var pop=func(vec){
return __builtin_pop(vec);
}
# str is used to change number into string.
var str=func(num){
return __builtin_str(num);
}
# size can get the size of a string/vector/hashmap.
# in fact it can also get the size of number, and the result is the number itself.
# so don't do useless things, though it really works.
var size=func(object){
return __builtin_size(object);
}
# contains is used to check if a key exists in a hashmap/dict.
var contains=func(hash,key){
return __builtin_contains(hash,key);
}
# delete is used to delete a pair in a hashmap/dict by key.
var delete=func(hash,key){
return __builtin_delete(hash,key);
}
# keys is used to get all keys in a hashmap/dict.
# this function will return a vector.
var keys=func(hash){
return __builtin_keys(hash);
}
# time has the same function in C.
var time=func(begin){
return __builtin_time(begin);
}
# die is a special native function.
# use it at where you want the program to crash immediately.
var die=func(str){
return __builtin_die(str);
}
# typeof is used to get the type of an object.
# this function returns a string.
var typeof=func(object){
return __builtin_type(object);
}
# substr will get the sub-string.
# it gets the string, the begin index and sub-string's length as arguments.
var substr=func(str,begin,len){
return __builtin_substr(str,begin,len);
}
# streq is used to compare if two strings are the same.
var streq=func(a,b){
return __builtin_streq(a,b);
}
# left is used to get the sub-string like substr.
# but the begin index is 0.
var left=func(str,len){
return __builtin_left(str,len);
}
# right i used to get the sub-string like substr.
# but the begin index is strlen-len.
var right=func(str,len){
return __builtin_right(str,len);
}
# cmp is used to compare two strings.
# normal string will not be correctly compared by operators < > <= >=
# because these operators will turn strings into numbers then compare.
var cmp=func(a,b){
return __builtin_cmp(a,b);
}
# chr is used to get the character by ascii-number.
# for example chr(65) -> 'A'
var chr=func(code){
return __builtin_chr(code);
}
# println has the same function as print.
# but it will output a '\n' after using print.
var println=func(elems...){
__builtin_print(elems);
elems=['\n'];
return __builtin_print(elems);
}
var append=func(vec,elems...)
{
return __builtin_append(vec,elems);
}
var setsize=func(vec,size)
{
return __builtin_setsize(vec,size);
}
var system=func(str)
{
return __builtin_system(str);
}
var input=func()
{
return __builtin_input();
}
var sleep=func(duration)
{
return __builtin_sleep(duration);
}
var split=func(deli,str)
{
return __builtin_split(deli,str);
}
var rand=func(seed=nil)
{
return __builtin_rand(seed);
}
var id=func(object)
{
return __builtin_id(object);
}
var int=func(val)
{
return __builtin_int(val);
}
var num=func(val)
{
return __builtin_num(val);
}
var pop=func(vec)
{
return __builtin_pop(vec);
}
var str=func(num)
{
return __builtin_str(num);
}
var size=func(object)
{
return __builtin_size(object);
}
var contains=func(hash,key)
{
return __builtin_contains(hash,key);
}
var delete=func(hash,key)
{
return __builtin_delete(hash,key);
}
var keys=func(hash)
{
return __builtin_keys(hash);
}
var time=func(begin_time)
{
return __builtin_time(begin_time);
}
var die=func(str)
{
return __builtin_die(str);
}
var typeof=func(object)
{
return __builtin_type(object);
}
var substr=func(str,begin,len)
{
return __builtin_substr(str,begin,len);
}
var streq=func(a,b)
{
return __builtin_streq(a,b);
}
var left=func(str,len)
{
return __builtin_left(str,len);
}
var right=func(str,len)
{
return __builtin_right(str,len);
}
var cmp=func(a,b)
{
return __builtin_cmp(a,b);
}
var chr=func(code)
{
return __builtin_chr(code);
}
var io=
{
fin: func(filename){return __builtin_fin(filename);},
fout:func(filename,str){return __builtin_fout(filename,str);}
SEEK_SET:0,
SEEK_CUR:1,
SEEK_END:2,
# get content of a file by filename. returns a string.
fin: func(filename){return __builtin_fin(filename);},
# input a string as the content of a file.
fout: func(filename,str){return __builtin_fout(filename,str);},
# same as C fopen. open file and get the FILE*.
open: func(filename,mode="r"){return __builtin_open(filename,mode);},
# same as C fclose. close file by FILE*.
close: func(filehandle){return __builtin_close(filehandle);},
# same as C fread. read file by FILE*.
read: func(filehandle,buf,len){return __builtin_read(filehandle,buf,len);},
# same as C fwrite. write file by FILE*.
write: func(filehandle,str){return __builtin_write(filehandle,str);},
# same as C fseek. seek place by FILE*.
seek: func(filehandle,pos,whence){return __builtin_seek(filehandle,pos,whence);},
# same as C ftell.
tell: func(filehandle){return __builtin_tell(filehandle);},
# read file by lines. use FILE*.
# get nil if EOF
readln:func(filehandle){return __builtin_readln(filehandle);},
# same as C stat.
stat: func(filename){return __builtin_stat(filename);},
# same as C feof. check if FILE* gets the end of file(EOF).
eof: func(filehandle){return __builtin_eof(filehandle);}
};
# get file status. using data from io.stat
var fstat=func(filename){
var s=io.stat(filename);
return {
st_dev: s[0],
st_ino: s[1],
st_mode: s[2],
st_nlink:s[3],
st_uid: s[4],
st_gid: s[5],
st_rdev: s[6],
st_size: s[7],
st_atime:s[8],
st_mtime:s[9],
st_ctime:s[10]
};
}
# functions that do bitwise calculation.
# carefully use it, all the calculations are based on integer.
var bits=
{
# xor
bitxor: func(a,b){return __builtin_xor(a,b); },
# and
bitand: func(a,b){return __builtin_and(a,b); },
# or
bitor: func(a,b){return __builtin_or(a,b); },
# nand
bitnand: func(a,b){return __builtin_nand(a,b);},
bitnot: func(a) {return __builtin_not(a); }
# not
bitnot: func(a) {return __builtin_not(a); },
# get bit data from a special string. for example:
# bits.fld(s,0,3);
# if s stores 10100010(162)
# will get 101(5).
fld: func(str,startbit,len){return __builtin_fld;},
# get sign-extended data from a special string. for example:
# bits.sfld(s,0,3);
# if s stores 10100010(162)
# will get 101(5) then this will be signed extended to
# 11111101(-3).
sfld: func(str,startbit,len){return __builtin_sfld;},
# set value into a special string to store it. little-endian, for example:
# bits.setfld(s,0,8,69);
# set 01000101(69) to string will get this:
# 10100010(162)
# so s[0]=162.
setfld: func(str,startbit,len,val){return __builtin_setfld;},
# get a special string filled by '\0' to use in setfld.
buf: func(len){return __builtin_buf;}
};
# mostly used math functions and special constants, you know.
var math=
{
e: 2.7182818284590452354,
pi: 3.14159265358979323846264338327950288,
inf: 1/0,
nan: 0/0,
sin: func(x) {return __builtin_sin(x); },
cos: func(x) {return __builtin_cos(x); },
tan: func(x) {return __builtin_tan(x); },
exp: func(x) {return __builtin_exp(x); },
exp: func(x) {return __builtin_exp(x); },
lg: func(x) {return __builtin_lg(x); },
ln: func(x) {return __builtin_ln(x); },
sqrt: func(x) {return __builtin_sqrt(x); },
atan2: func(x,y){return __builtin_atan2(x,y);},
inf: 1/0,
nan: 0/0,
isnan: func(x) {return __builtin_isnan(x); }
};
var unix=
{
pipe: func(){die("not supported yet");},
fork: func(){die("not supported yet");},
dup2: func(fd0,fd1){die("not supported yet");},
exec: func(filename,argv,envp){die("not supported yet");},
waitpid: func(pid,nohang=0){die("not supported yet");},
isdir: func(path){return bits.bitand(io.stat(path)[2],0x4000);}, # S_IFDIR 0x4000
isfile: func(path){return bits.bitand(io.stat(path)[2],0x8000);}, # S_IFREG 0x8000
opendir: func(path){return __builtin_opendir;},
readdir: func(handle){return __builtin_readdir;},
closedir: func(handle){return __builtin_closedir;},
time: func(){return time(0);},
sleep: func(secs){return __builtin_sleep(secs);},
chdir: func(path){return __builtin_chdir(path);},
environ: func(){die("not supported yet");},
getcwd: func(){return __builtin_getcwd();},
getenv: func(envvar){return __builtin_getenv(envvar);}
};
# dylib is the core hashmap for developers to load their own library.
var dylib=
{
# open dynamic lib.
dlopen: func(libname){return __builtin_dlopen;},
# load symbol from an open dynamic lib.
dlsym: func(lib,sym){return __builtin_dlsym; },
# close dynamic lib, this operation will make all the symbols loaded from it invalid.
dlclose: func(lib){return __builtin_dlclose; },
# call the loaded symbol.
dlcall: func(funcptr,args...){return __builtin_dlcall}
};
# os is used to use or get some os-related info/functions.
# windows/macOS/linux are supported.
var os=
{
# get a string that tell which os it runs on.
platform: func(){return __builtin_platform;}
};
# runtime gives us some functions that we could manage it manually.
var runtime=
{
# do garbage collection manually.
# carefully use it because using it frequently may make program running slower.
gc: func(){return __builtin_gc;}
};
# important global constants
var D2R=math.pi/180;
var FPS2KT=0.5925;
var FT2M=0.3048;

View File

@@ -3,65 +3,50 @@
var list=func()
{
var (begin,end)=(nil,nil);
return
{
push_back:func(elem)
{
return{
push_back:func(elem){
var tmp={elem:elem,prev:nil,next:nil};
if(end!=nil)
{
if(end!=nil){
end.next=tmp;
tmp.prev=end;
end=tmp;
}
else
begin=end=tmp;
return;
},
push_front:func(elem)
{
push_front:func(elem){
var tmp={elem:elem,prev:nil,next:nil};
if(begin!=nil)
{
if(begin!=nil){
begin.prev=tmp;
tmp.next=begin;
begin=tmp;
}
else
begin=end=tmp;
return;
},
pop_back:func()
{
pop_back:func(){
if(end!=nil)
end=end.prev;
if(end==nil)
begin=nil;
else
end.next=nil;
return;
},
pop_front:func()
{
pop_front:func(){
if(begin!=nil)
begin=begin.next;
if(begin==nil)
end=nil;
else
begin.prev=nil;
return;
},
front:func()
{
front:func(){
if(begin!=nil)
return begin.elem;
return nil;
},
back:func()
{
back:func(){
if(end!=nil)
return end.elem;
return nil;
},
};
}

View File

@@ -3,43 +3,33 @@
var queue=func()
{
var (begin,end)=(nil,nil);
return
{
push:func(elem)
{
var new_node=
{
return{
push:func(elem){
var new_node={
elem:elem,
next:nil
};
if(begin==nil)
begin=end=new_node;
else
{
else{
end.next=new_node;
end=new_node;
}
return;
},
pop:func()
{
pop:func(){
if(begin!=nil)
begin=begin.next;
if(begin==nil)
end=nil;
},
front:func()
{
front:func(){
if(begin!=nil)
return begin.elem;
return nil;
},
clear:func()
{
clear:func(){
begin=end=nil;
},
empty:func()
{
empty:func(){
return begin==nil;
}
};

View File

@@ -13,7 +13,7 @@ var ResultTrait={
},
unwrap:func(){
if(me.flag)
die("error: "~me.err);
die(me.err);
return me.ok;
}
};

View File

@@ -3,33 +3,22 @@
var stack=func()
{
var next=nil;
return
{
push:func(elem)
{
return{
push:func(elem){
next={elem:elem,next:next};
return;
},
pop:func()
{
var tmp=next;
if(tmp!=nil)
next=tmp.next;
return;
pop:func(){
if(next!=nil)
next=next.next;
},
top:func()
{
var tmp=next;
if(tmp!=nil)
return tmp.elem;
return nil;
top:func(){
if(next!=nil)
return next.elem;
},
clear:func()
{
clear:func(){
next=nil;
},
empty:func()
{
empty:func(){
return next==nil;
}
};

View File

@@ -149,94 +149,78 @@ var mandelbrot=
var paper=[];
var (ptr,pc)=(0,0);
var (code,inum,stack)=(nil,nil,nil);
var (code,inum,stack)=([],[],[]);
var (add,mov,jt,jf,in,out)=(0,1,2,3,4,5);
var func_table=[
func paper[ptr]+=inum[pc],
func ptr+=inum[pc],
func if(paper[ptr])pc=inum[pc],
func if(!paper[ptr])pc=inum[pc],
func paper[ptr]=input()[0],
func print(chr(paper[ptr]))
];
var bf=func(program)
{
var bf=func(program){
setsize(paper,131072);
(ptr,code,inum,stack)=(0,[],[],[]);
var len=size(program);
for(var i=0;i<len;i+=1)
{
for(var i=0;i<len;i+=1){
var c=chr(program[i]);
if(c=='+' or c=='-')
{
append(code,func_table[add]);
if(c=='+' or c=='-'){
append(code,add);
append(inum,0);
for(;i<len;i+=1)
{
for(;i<len;i+=1){
if(chr(program[i])=='+')
inum[-1]+=1;
elsif(chr(program[i])=='-')
inum[-1]-=1;
elsif(chr(program[i])!='\n')
{
elsif(chr(program[i])!='\n'){
i-=1;
break;
}
}
}
elsif(c=='<' or c=='>')
{
append(code,func_table[mov]);
elsif(c=='<' or c=='>'){
append(code,mov);
append(inum,0);
for(;i<len;i+=1)
{
for(;i<len;i+=1){
if(chr(program[i])=='>')
inum[-1]+=1;
elsif(chr(program[i])=='<')
inum[-1]-=1;
elsif(chr(program[i])!='\n')
{
elsif(chr(program[i])!='\n'){
i-=1;
break;
}
}
}
elsif(c==',')
{
append(code,func_table[in]);
elsif(c==','){
append(code,in);
append(inum,0);
}
elsif(c=='.')
{
append(code,func_table[out]);
elsif(c=='.'){
append(code,out);
append(inum,0);
}
elsif(c=='[')
{
append(code,func_table[jf]);
elsif(c=='['){
append(code,jf);
append(inum,0);
append(stack,size(code)-1);
}
elsif(c==']')
{
elsif(c==']'){
if(!size(stack))
die("lack [");
var label=pop(stack);
append(code,func_table[jt]);
append(code,jt);
append(inum,label-1);
inum[label]=size(code)-2;
}
}
if(size(stack))
{
if(size(stack)){
die("lack ]");
return;
}
len=size(code);
for(pc=0;pc<len;pc+=1)
code[pc]();
for(pc=0;pc<len;pc+=1){
var c=code[pc];
if(c==add) paper[ptr]+=inum[pc];
elsif(c==mov)ptr+=inum[pc];
elsif(c==jt){if(paper[ptr])pc=inum[pc];}
elsif(c==jf){if(!paper[ptr])pc=inum[pc];}
elsif(c==in) paper[ptr]=input()[0];
else print(chr(paper[ptr]));
}
return;
}

View File

@@ -8,20 +8,25 @@ var map=[];
for(var i=0;i<10;i+=1)
{
append(map,[]);
for(var j=0;j<10;j+=1)
for(var j=0;j<20;j+=1)
append(map[i],(rand()>0.7));
}
var prt=func()
{
var s="";
if(os.platform()=="windows")
system("cls");
else
system("clear");
var s="+--------------------+\n";
for(var i=0;i<10;i+=1)
{
for(var j=0;j<10;j+=1)
s~="|";
for(var j=0;j<20;j+=1)
s~=pixel[map[i][j]];
s~='\n';
s~='|\n';
}
s~='----------\n';
s~='+--------------------+\n';
print(s);
}
@@ -31,6 +36,7 @@ var bfs=func(begin,end)
var que=queue();
que.push(begin);
map[begin[0]][begin[1]]=2;
map[end[0]][end[1]]=0;
while(!que.empty())
{
var vertex=que.front();
@@ -45,7 +51,7 @@ var bfs=func(begin,end)
prt();
return;
}
if(0<=x and x<10 and 0<=y and y<10 and map[x][y]==0)
if(0<=x and x<10 and 0<=y and y<20 and map[x][y]==0)
{
que.push([x,y]);
map[x][y]=2;
@@ -57,4 +63,4 @@ var bfs=func(begin,end)
return;
}
bfs([0,0],[9,9]);
bfs([0,0],[9,19]);

View File

@@ -1,32 +1,96 @@
import("lib.nas");
var filename=["main.cpp","nasal_ast.h","nasal_builtin.h","nasal_codegen.h","nasal_gc.h","nasal_import.h","nasal_lexer.h","nasal_parse.h","nasal_vm.h","nasal.h"];
var space=[" "," ","",""," "," "," "," "," "," "];
var enter_cnt=func(s)
{
var (cnt,len,enter)=(0,size(s),'\n'[0]);
var source=[
"main.cpp ",
"nasal_err.h ",
"nasal_ast.h ",
"nasal_builtin.h ",
"nasal_codegen.h ",
"nasal_opt.h ",
"nasal_gc.h ",
"nasal_import.h ",
"nasal_lexer.h ",
"nasal_parse.h ",
"nasal_vm.h ",
"nasal_dbg.h ",
"nasal.h "
];
var lib=[
"stl/lib.nas ",
"stl/list.nas ",
"stl/queue.nas ",
"stl/result.nas ",
"stl/sort.nas ",
"stl/stack.nas "
];
var testfile=[
"test/ascii-art.nas ",
"test/auto_crash.nas ",
"test/bf.nas ",
"test/bfconvertor.nas ",
"test/bfs.nas ",
"test/bigloop.nas ",
"test/bp.nas ",
"test/calc.nas ",
"test/choice.nas ",
"test/class.nas ",
"test/exception.nas ",
"test/fib.nas ",
"test/filesystem.nas ",
"test/hexdump.nas ",
"test/json.nas ",
"test/leetcode1319.nas ",
"test/lexer.nas ",
"test/life.nas ",
"test/loop.nas ",
"test/mandel.nas ",
"test/mandelbrot.nas ",
"test/module_test.nas ",
"test/nasal_test.nas ",
"test/pi.nas ",
"test/prime.nas ",
"test/props.nas ",
"test/quick_sort.nas ",
"test/scalar.nas ",
"test/trait.nas ",
"test/turingmachine.nas",
"test/ycombinator.nas "
];
var module=[
"module/fib.cpp "
];
var getname=func(s){
var (len,ch)=(size(s),' '[0]);
for(var i=0;i<len and s[i]!=ch;i+=1);
return substr(s,0,i);
}
var count=func(s,c){
var (cnt,len,ch)=(0,size(s),c[0]);
for(var i=0;i<len;i+=1)
cnt+=(s[i]==enter);
cnt+=(s[i]==ch);
return cnt;
}
var semic_cnt=func(s)
{
var (cnt,len,semi)=(0,size(s),';'[0]);
for(var i=0;i<len;i+=1)
cnt+=(s[i]==semi);
return cnt;
}
func(){
var (bytes,line,semi)=(0,0,0);
forindex(var i;filename)
{
var s=io.fin(filename[i]);
var calc=func(codetype,files){
println(codetype);
var (bytes,line,semi,line_cnt,semi_cnt)=(0,0,0,0,0);
forindex(var i;files){
var s=io.fin(getname(files[i]));
(line_cnt,semi_cnt)=(count(s,'\n'),count(s,';'));
println(files[i],'| ',line_cnt,' \tline | ',semi_cnt,' \tsemi');
bytes+=size(s);
var line_cnt=enter_cnt(s);
var semi_cnt=semic_cnt(s);
println(filename[i],space[i],'| ',line_cnt,' \tline | ',semi_cnt,' \tsemi');
line+=line_cnt;
semi+=semi_cnt;
}
println('total: | ',line,' \tline | ',semi,' \tsemi');
println('bytes: | ',bytes,' bytes| ',int(bytes/1024),' \tkb');
}();
println('total: | ',line,' \tline | ',semi,' \tsemi');
println('bytes: | ',bytes,'\tbytes| ',int(bytes/1024),' \tkb');
}
calc("source code:",source);
calc("lib:",lib);
calc("test file:",testfile);
calc("module:",module);

View File

@@ -1,8 +1,6 @@
import("lib.nas");
var student=func(name,age)
{
var (n,a)=(name,age);
var student=func(n,a){
return {
print_info:func println(n,' ',a),
set_age: func(age) a=age,
@@ -63,6 +61,4 @@ a(); # 2048 1024 -1
b(); # 2048 1024 1 1
m.h()(2147483647);
m.a();
# flightgear-nasal-console: 2147483647 1024
# nasal-interpreter 2048 1024
m.a();# 2147483647 1024

View File

@@ -13,7 +13,7 @@ var ResultTrait={
},
unwrap:func(){
if(me.flag)
die("error: "~me.err);
die(me.err);
return me.ok;
}
};
@@ -32,7 +32,7 @@ var a=func(){
}
var b=func(){
return Result().Err("unknown");
return Result().Err("exception test");
}
println(a().unwrap());

36
test/filesystem.nas Normal file
View File

@@ -0,0 +1,36 @@
import("lib.nas");
var fd=io.open("test/filesystem.nas");
while((var line=io.readln(fd))!=nil)
println(line);
io.close(fd);
println(io.stat("test/filesystem.nas"));
var dd=unix.opendir("test");
while(var name=unix.readdir(dd))
println(name);
unix.closedir(dd);
var files=func(dir){
var dd=unix.opendir(dir);
var res=[];
while(var n=unix.readdir(dd))
append(res,n);
unix.closedir(dd);
return res;
}
var prt=func(s,path){
foreach(var i;files(path)){
print(s,i);
if(unix.isdir(path~'/'~i)){
print(' <dir>\n');
if(i!='.' and i!='..')
prt(s~' |',path~'/'~i);
}
elsif(unix.isfile(path~'/'~i))
print(" <file>\n");
else
print(' <unknown>\n');
}
}
prt('',".");

View File

@@ -16,8 +16,7 @@ foreach(var i;hex_num)
append(hex,i~j);
# read file
var s=func()
{
var s=func(){
var filename=[
"nasal.h",
"main.cpp",
@@ -43,49 +42,45 @@ var cnt=0;
var hex_index=[0,0,0,0];
# print binary in text format
var textprint=func(index)
{
print(' |');
var textprint=func(index){
var info='';
for(var i=index-cnt;i<index;i+=1)
print((0<=s[i] and s[i]<32)?'.':chr(s[i]));
info~=(0<=s[i] and s[i]<32)?'.':chr(s[i]);
for(var i=cnt;i<16;i+=1)
print('.');
print('|\n');
info~='.';
return ' |'~info~'|\n';
}
# print index
var indexprint=func(index)
{
forindex(var i;hex_index)
{
var indexprint=func(index){
forindex(var i;hex_index){
hex_index[i]=index-int(index/256)*256;
index=int(index/256);
}
var info='';
for(var i=3;i>=0;i-=1)
print(hex[hex_index[i]]);
print(' ');
return;
info~=hex[hex_index[i]];
return info~' ';
}
# main
func()
{
indexprint(0);
for(var i=0;i<size(s);i+=1)
{
func(){
var info=indexprint(0);
for(var i=0;i<size(s);i+=1){
if(cnt==16){
textprint(i);
info~=textprint(i);
print(info);
cnt=0;
indexprint(i);
}elsif(cnt==8){
print(' ');
}
info=indexprint(i);
}elsif(cnt==8)
info~=' ';
cnt+=1;
print(hex[s[i]],' ');
info~=hex[s[i]]~' ';
}
for(var l=cnt;l<16;l+=1)
print(' ');
info~=' ';
if(cnt<=8)
print(' ');
textprint(i);
info~=' ';
info~=textprint(i);
print(info);
}();

View File

@@ -161,7 +161,7 @@ var lexer=func(file)
ptr+=1;
return;
},
main:func()
compile:func()
{
while(ptr<len)
{
@@ -185,9 +185,8 @@ var lexer=func(file)
};
}
var nasal_lexer=lexer("test/lexer.nas");
nasal_lexer.main();
var info="";
foreach(var tok;nasal_lexer.get_token())
info~=tok~' ';
println(info);
var lex=lexer("test/props.nas");
lex.compile();
foreach(var tok;lex.get_token())
print(tok,' ');
print('\n');

View File

@@ -30,7 +30,10 @@ var prt=func()
s~=elem~' ';
s~='\n';
}
system("cls");
if(os.platform()=="windows")
system("cls");
else
system("clear");
print(s);
}

View File

@@ -7,25 +7,32 @@ for(;;)
}
for(var i=1;;)break;
for(var i=1;;i+=1)break;
for(var i=1;i<10;i+=1)print(i);
for(var i=1;i<10;i+=1)print(i,'\n');
while(1)break;
var j=0;
while(j<10)
{
print(j);
print(j,'\n');
j+=1;
}
forindex(var j;[0,1,2,3])print(j);
forindex(var j;[0,1,2,3])print(j,'\n');
forindex(var j;[0,1,2,3])
{
var a=j;
print(a*a);
print(a*a,'\n');
}
foreach(var j;[0,1,2,3])print([0,1,2,3][j]);
foreach(var j;[0,1,2,3])print([0,1,2,3][j],'\n');
foreach(var j;[0,1,2,3])
{
var a=[0,1,2,3][j];
print(a*a-1);
print(a*a-1,'\n');
}
var f=func(){
var x=0;
return func(){x+=1;};
}();
for(var i=0;i<4e6;i+=1)
f();

4893
test/mandel.nas Normal file

File diff suppressed because it is too large Load Diff

17
test/module_test.nas Normal file
View File

@@ -0,0 +1,17 @@
import("lib.nas");
var libfib=func(){
var dl=dylib.dlopen("./module/libfib."~(os.platform()=="windows"?"dll":"so"));
var fib=dylib.dlsym(dl,"fib");
var qfib=dylib.dlsym(dl,"quick_fib");
var call=dylib.dlcall;
return
{
fib: func(x){return call(fib,x)},
qfib:func(x){return call(qfib,x)}
};
}();
println(libfib);
println(libfib.fib(29));
println(libfib.qfib(29));

View File

@@ -3,7 +3,7 @@ import("lib.nas");
var (t,res)=(1,0);
for(var m=1;m<4e6;m+=2)
{
res+=t*1/m;
res+=t/m;
t=-t;
}
println(res*4);

View File

@@ -19,7 +19,7 @@ var sort=func(vec,left,right)
}
var vec=[];
rand(time(0));
for(var i=0;i<200;i+=1)
append(vec,int(rand()*1000));
for(var i=0;i<1e4;i+=1)
append(vec,int(rand()*1e5));
sort(vec,0,size(vec)-1);
println(vec);

51
test/turingmachine.nas Normal file
View File

@@ -0,0 +1,51 @@
import("lib.nas");
var table=[
['q0','0','1','R','q1'],
['q1','1','1','R','q1'],
['q1','0','0','S','q2'],
['q2','0','1','R','q3'],
['q3',nil,nil,'S','q3']
];
var prt=func(state,pointer,paper,act=nil){
print(state,':',pointer,':',act!=nil?act:'','\n\t');
var s='';
foreach(var i;paper)
s~=i;
s~='\n\t';
for(var i=0;i<pointer;i+=1)
for(var j=0;j<size(paper[i]);j+=1)
s~=' ';
print(s,'^\n');
}
var run=func(table,node,start,stop){
var paper=['0','1','1','1','0','1','0','a'];
var pointer=0;
foreach(var action;table){
if(!contains(node,action[0]))
node[action[0]]=nil;
if(!contains(node,action[4]))
node[action[4]]=nil;
}
print("nodes: ",keys(node),'\n');
if(!contains(node,start))
die(start~" is not a valid node");
if(!contains(node,stop))
die(stop~" is not a valid node");
var state=start;
prt(state,pointer,paper);
while(state!=stop){
foreach(var action;table)
if(action[0]==state and (action[1]==paper[pointer] or action[1]==' ')){
paper[pointer]=action[2]==nil?paper[pointer]:action[2];
if(action[3]=='L') pointer-=1;
elsif(action[3]=='R') pointer+=1;
elsif(action[3]!='S') die("invalid action <"~action[3]~'>');
state=action[4];
break;
}
prt(state,pointer,paper,action);
}
}
run(table,{},'q0','q3');

View File

@@ -8,10 +8,11 @@ var fib=func(f){
func(f){
return func(x){
if(x<2) return x;
return f(f)(x-1)+f(f)(x-2);
var tmp=f(f);
return tmp(x-1)+tmp(x-2);
}
}
);
for(var i=1;i<31;i+=1)
println(fib(i));
println(fib(i));