change build scripts

This commit is contained in:
ValKmjolnir 2023-07-02 19:48:36 +08:00
parent ba6b7cd05c
commit ac2744e24f
32 changed files with 231 additions and 8289 deletions

View File

@ -16,9 +16,9 @@ jobs:
- uses: actions/checkout@v2
- name: make
run: |
make -j
make -j4
cd module
make all -j
make all -j4
cd ..
make test
tar -czf nasal-mac-nightly.tgz .
@ -42,9 +42,9 @@ jobs:
- uses: actions/checkout@v2
- name: make
run: |
make -j
make -j4
cd module
make all -j
make all -j4
cd ..
make test
touch nasal-linux-x86_64-nightly.tgz

View File

@ -9,22 +9,12 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(CMAKE_CXX_FLAGS_RELEASE_INIT "-Wshadow -Wall")
add_compile_options(-fPIC)
# generate release executables
set(CMAKE_BUILD_TYPE "Release")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/module)
add_library(fib SHARED ${CMAKE_SOURCE_DIR}/module/fib.cpp)
target_include_directories(fib PRIVATE ${CMAKE_SOURCE_DIR}/src)
add_library(key SHARED ${CMAKE_SOURCE_DIR}/module/keyboard.cpp)
target_include_directories(key PRIVATE ${CMAKE_SOURCE_DIR}/src)
add_library(mat SHARED ${CMAKE_SOURCE_DIR}/module/matrix.cpp)
target_include_directories(mat PRIVATE ${CMAKE_SOURCE_DIR}/src)
add_library(nasock SHARED ${CMAKE_SOURCE_DIR}/module/nasocket.cpp)
target_include_directories(nasock PRIVATE ${CMAKE_SOURCE_DIR}/src)
# build nasal used object
set(NASAL_OBJECT_SOURCE_FILE
${CMAKE_SOURCE_DIR}/src/ast_dumper.cpp
${CMAKE_SOURCE_DIR}/src/ast_visitor.cpp
@ -42,18 +32,16 @@ set(NASAL_OBJECT_SOURCE_FILE
${CMAKE_SOURCE_DIR}/src/nasal_new_vm.cpp
${CMAKE_SOURCE_DIR}/src/optimizer.cpp
${CMAKE_SOURCE_DIR}/src/symbol_finder.cpp)
add_executable(nasal ${CMAKE_SOURCE_DIR}/src/nasal_new_main.cpp)
add_library(nasal-object STATIC ${NASAL_OBJECT_SOURCE_FILE})
target_include_directories(nasal-object PRIVATE ${CMAKE_SOURCE_DIR}/src)
# build nasal
add_executable(nasal ${CMAKE_SOURCE_DIR}/src/nasal_new_main.cpp)
target_link_libraries(nasal nasal-object)
if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
target_link_libraries(nasal dl)
endif()
target_include_directories(nasal PRIVATE ${CMAKE_SOURCE_DIR}/src)
if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
add_custom_command(
TARGET nasal POST_BUILD
@ -62,3 +50,27 @@ if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
${CMAKE_SOURCE_DIR}/nasal
)
endif()
# build module
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/module)
set(MODULE_USED_OBJECT_SOURCE_FILE
${CMAKE_SOURCE_DIR}/src/nasal_new_misc.cpp
${CMAKE_SOURCE_DIR}/src/nasal_new_gc.cpp)
add_library(module-used-object STATIC ${MODULE_USED_OBJECT_SOURCE_FILE})
add_library(fib SHARED ${CMAKE_SOURCE_DIR}/module/fib.cpp)
target_include_directories(fib PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_link_libraries(fib module-used-object)
add_library(key SHARED ${CMAKE_SOURCE_DIR}/module/keyboard.cpp)
target_include_directories(key PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_link_libraries(key module-used-object)
add_library(mat SHARED ${CMAKE_SOURCE_DIR}/module/matrix.cpp)
target_include_directories(mat PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_link_libraries(mat module-used-object)
add_library(nasock SHARED ${CMAKE_SOURCE_DIR}/module/nasocket.cpp)
target_include_directories(nasock PRIVATE ${CMAKE_SOURCE_DIR}/src)
target_link_libraries(nasock module-used-object)

178
main.cpp
View File

@ -1,178 +0,0 @@
#include "nasal.h"
#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"
#include <thread>
#include <unordered_map>
const u32 VM_AST =0x01;
const u32 VM_CODE =0x02;
const u32 VM_TIME =0x04;
const u32 VM_EXEC =0x08;
const u32 VM_DETAIL=0x10;
const u32 VM_DEBUG =0x20;
std::ostream& help(std::ostream& out) {
out
<<" ,--#-,\n"
<<"<3 / \\____\\ <3\n"
<<" |_|__A_|\n"
#ifdef _WIN32
<<"use command <chcp 65001> to use unicode.\n"
#endif
<<"\nnasal <option>\n"
<<"option:\n"
<<" -h, --help | get help.\n"
<<"\nnasal [option] <file> [argv]\n"
<<"option:\n"
<<" -a, --ast | view abstract syntax tree.\n"
<<" -c, --code | view bytecode.\n"
<<" -e, --exec | execute.\n"
<<" -t, --time | show execute time.\n"
<<" -d, --detail | get detail info.\n"
<<" -dbg, --debug | debug mode.\n"
<<"file:\n"
<<" <filename> | execute file.\n"
<<"argv:\n"
<<" <args> | cmd arguments used in program.\n";
return out;
}
std::ostream& logo(std::ostream& out) {
out
<<" __ _\n"
<<" /\\ \\ \\__ _ ___ __ _| |\n"
<<" / \\/ / _` / __|/ _` | |\n"
<<" / /\\ / (_| \\__ \\ (_| | |\n"
<<" \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
<<"ver : "<<__nasver<<" ("<<__DATE__<<" "<<__TIME__<<")\n"
<<"std : c++ "<<__cplusplus<<"\n"
<<"core : "<<std::thread::hardware_concurrency()<<" core(s)\n"
<<"repo : https://github.com/ValKmjolnir/Nasal-Interpreter\n"
<<"repo : https://gitee.com/valkmjolnir/Nasal-Interpreter\n"
<<"wiki : https://wiki.flightgear.org/Nasal_scripting_language\n"
<<"input <nasal -h> to get help .\n";
return out;
}
[[noreturn]]
void err() {
std::cerr
<<"invalid argument(s).\n"
<<"use <nasal -h> to get help.\n";
std::exit(1);
}
void execute(
const string& file,
const std::vector<string>& argv,
const u32 cmd
) {
using clk=std::chrono::high_resolution_clock;
const auto den=clk::duration::period::den;
error err;
lexer lex(err);
parse parse(err);
linker ld(err);
codegen gen(err);
vm ctx;
// lexer scans file to get tokens
lex.scan(file).chkerr();
// parser gets lexer's token list to compile
parse.compile(lex).chkerr();
if (cmd&VM_AST) {
parse.tree().dump();
}
// linker gets parser's ast and load import files to this ast
ld.link(parse, file, cmd&VM_DETAIL).chkerr();
// optimizer does simple optimization on ast
optimize(parse.tree());
// code generator gets parser's ast and import file list to generate code
gen.compile(parse, ld).chkerr();
if (cmd&VM_CODE) {
gen.print();
}
// run
auto start=clk::now();
if (cmd&VM_DEBUG) {
dbg(err).run(gen, ld, argv);
} else if (cmd&VM_TIME || cmd&VM_EXEC) {
ctx.run(gen, ld, argv, cmd&VM_DETAIL);
}
// get running time
if (cmd&VM_TIME) {
f64 tm=(clk::now()-start).count()*1.0/den;
std::clog<<"process exited after "<<tm<<"s.\n\n";
}
}
i32 main(i32 argc, const char* argv[]) {
// output version info
if (argc<=1) {
std::clog<<logo;
return 0;
}
// run directly or show help
if (argc==2) {
string s(argv[1]);
if (s=="-h" || s=="--help") {
std::clog<<help;
} else if (s[0]!='-') {
execute(s, {}, VM_EXEC);
} else {
err();
}
return 0;
}
// execute with arguments
const std::unordered_map<string,u32> cmdlst={
{"--ast", VM_AST},
{"-a", VM_AST},
{"--code", VM_CODE},
{"-c", VM_CODE},
{"--exec", VM_EXEC},
{"-e", VM_EXEC},
{"--time", VM_TIME|VM_EXEC},
{"-t", VM_TIME|VM_EXEC},
{"--detail", VM_DETAIL|VM_EXEC},
{"-d", VM_DETAIL|VM_EXEC},
{"--debug", VM_DEBUG},
{"-dbg", VM_DEBUG}
};
u32 cmd=0;
string filename="";
std::vector<string> vm_argv;
for(i32 i=1; i<argc; ++i) {
if (cmdlst.count(argv[i])) {
cmd|=cmdlst.at(argv[i]);
} else if (!filename.length()) {
filename=argv[i];
} else {
vm_argv.push_back(argv[i]);
}
}
if (!filename.length()) {
err();
}
execute(filename, vm_argv, cmd?cmd:VM_EXEC);
return 0;
}

View File

@ -1,5 +1,3 @@
.PHONY:test clean
STD=c++17
NASAL_OBJECT=\
@ -83,38 +81,12 @@ nasal_new_dbg.o: src/nasal_new_dbg.h src/nasal_new_dbg.cpp
nasal_new_clean:
rm $(NASAL_OBJECT)
SRC=\
main.cpp\
nasal_ast.h\
nasal_err.h\
nasal_builtin.h\
nasal_opcode.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
nasal_old:$(SRC)
$(CXX) -std=$(STD) -O3 main.cpp -o nasal_old -fno-exceptions -ldl -Wshadow -Wall
nasal_old.exe:$(SRC)
$(CXX) -std=$(STD) -O3 main.cpp -o nasal_old.exe -fno-exceptions -Wshadow -Wall -static
stable-release:$(SRC)
$(CXX) -std=$(STD) -O2 main.cpp -o nasal_old -fno-exceptions -ldl -Wshadow -Wall
stable-release-mingw:$(SRC)
$(CXX) -std=$(STD) -O2 main.cpp -o nasal_old.exe -fno-exceptions -Wshadow -Wall -static
.PHONY: clean
clean:
@ echo "[clean] nasal" && if [ -e nasal ]; then rm nasal; fi
@ echo "[clean] nasal.exe" && if [ -e nasal.exe ]; then rm nasal.exe; fi
.PHONY: test
test:nasal
@ ./nasal -e test/ascii-art.nas
@ ./nasal -t -d test/bfs.nas

View File

@ -1,7 +1,7 @@
// module for test
#include <iostream>
#include "../nasal.h"
#include "../src/nasal_new_header.h"
namespace nasal_fib_module {

View File

@ -1,4 +1,4 @@
#include "../nasal.h"
#include "../src/nasal_new_header.h"
#include <iostream>
#ifndef _MSC_VER

View File

@ -1,9 +1,10 @@
.PHONY=clean all winall
dynamic_libs_so=libfib.so libkey.so libnasock.so libmat.so
dynamic_libs_dll=libfib.dll libkey.dll libnasock.dll libmat.dll
dynamic_libs_so = libfib.so libkey.so libnasock.so libmat.so
dynamic_libs_dll = libfib.dll libkey.dll libnasock.dll libmat.dll
used_header= ../src/nasal_new_header.h ../src/nasal_new_gc.h
used_header = ../src/nasal_new_header.h ../src/nasal_new_gc.h
used_object = ../nasal_new_misc.o ../nasal_new_gc.o
STD=c++17
@ -12,48 +13,48 @@ all: $(dynamic_libs_so)
winall: $(dynamic_libs_dll)
@ echo [Compiling] done
libfib.so: fib.cpp $(used_header)
libfib.so: fib.cpp $(used_header) $(used_object)
@ echo "[Compiling] libfib.so"
@ $(CXX) -std=$(STD) -c -O3 fib.cpp -fPIC -o fib.o
@ $(CXX) -shared -o libfib.so fib.o
@ $(CXX) -shared -o libfib.so fib.o $(used_object)
@ rm fib.o
libfib.dll: fib.cpp $(used_header)
libfib.dll: fib.cpp $(used_header) $(used_object)
@ echo [Compiling] libfib.dll
@ $(CXX) -std=$(STD) -c -O3 fib.cpp -fPIC -o fib.o
@ $(CXX) -shared -o libfib.dll fib.o
@ $(CXX) -shared -o libfib.dll fib.o $(used_object) -static
@ del fib.o
libkey.so: keyboard.cpp $(used_header)
libkey.so: keyboard.cpp $(used_header) $(used_object)
@ echo "[Compiling] libkey.so"
@ $(CXX) -std=$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o
@ $(CXX) -shared -o libkey.so keyboard.o
@ $(CXX) -shared -o libkey.so keyboard.o $(used_object)
@ rm keyboard.o
libkey.dll: keyboard.cpp $(used_header)
libkey.dll: keyboard.cpp $(used_header) $(used_object)
@ echo [Compiling] libkey.dll
@ $(CXX) -std=$(STD) -c -O3 keyboard.cpp -fPIC -o keyboard.o -static
@ $(CXX) -shared -o libkey.dll keyboard.o -static
@ $(CXX) -shared -o libkey.dll keyboard.o $(used_object) -static
@ del keyboard.o
libnasock.so: nasocket.cpp $(used_header)
libnasock.so: nasocket.cpp $(used_header) $(used_object)
@ echo "[Compiling] libnasock.so"
@ $(CXX) -std=$(STD) -c -O3 nasocket.cpp -fPIC -o nasocket.o
@ $(CXX) -shared -o libnasock.so nasocket.o
@ $(CXX) -shared -o libnasock.so nasocket.o $(used_object)
@ rm nasocket.o
libnasock.dll: nasocket.cpp $(used_header)
libnasock.dll: nasocket.cpp $(used_header) $(used_object)
@ echo [Compiling] libnasock.dll
@ $(CXX) -std=$(STD) -c -O3 nasocket.cpp -fPIC -o nasocket.o -lwsock32 -static
@ $(CXX) -shared -o libnasock.dll nasocket.o -lwsock32 -static
@ $(CXX) -shared -o libnasock.dll nasocket.o $(used_object) -lwsock32 -static
@ del nasocket.o
libmat.so: matrix.cpp $(used_header)
libmat.so: matrix.cpp $(used_header) $(used_object)
@ echo "[Compiling] libmat.so"
@ $(CXX) -std=$(STD) -c -O3 matrix.cpp -fPIC -o matrix.o
@ $(CXX) -shared -o libmat.so matrix.o
@ $(CXX) -shared -o libmat.so matrix.o $(used_object)
@ rm matrix.o
libmat.dll: matrix.cpp $(used_header)
libmat.dll: matrix.cpp $(used_header) $(used_object)
@ echo [Compiling] libmat.dll
@ $(CXX) -std=$(STD) -c -O3 matrix.cpp -fPIC -o matrix.o -static
@ $(CXX) -shared -o libmat.dll matrix.o -static
@ $(CXX) -shared -o libmat.dll matrix.o $(used_object) -static
@ del matrix.o
clean:

View File

@ -1,4 +1,4 @@
#include "../nasal.h"
#include "../src/nasal_new_header.h"
#include <cmath>
var nas_vec2(var* args, usize size, gc* ngc) {

View File

@ -1,4 +1,4 @@
#include "../nasal.h"
#include "../src/nasal_new_header.h"
#ifndef _MSC_VER
#include <unistd.h>

255
nasal.h
View File

@ -1,255 +0,0 @@
#pragma once
#ifndef __nasver
#define __nasver "10.1"
#endif
#include <cstdint>
#include <iostream>
#include <fstream>
#include <cstring>
#include <cmath>
#include <vector>
bool is_windows() {
#if defined(_WIN32) || defined(_WIN64)
return true;
#else
return false;
#endif
}
bool is_linux() {
#if defined __linux__
return true;
#else
return false;
#endif
}
bool is_macos() {
#if defined __APPLE__
return true;
#else
return false;
#endif
}
bool is_x86() {
#if defined(__i386__) || defined(_M_IX86)
return true;
#else
return false;
#endif
}
bool is_amd64() {
#if defined(__amd64__) || defined(_M_X64)
return true;
#else
return false;
#endif
}
bool is_x86_64() {
return is_amd64();
}
bool is_arm() {
#if defined(__arm__) || defined(_M_ARM)
return true;
#else
return false;
#endif
}
bool is_aarch64() {
#if defined(__aarch64__) || defined(_M_ARM64)
return true;
#else
return false;
#endif
}
bool is_ia64() {
#if defined(__ia64__)
return true;
#else
return false;
#endif
}
bool is_powerpc() {
#if defined(__powerpc__)
return true;
#else
return false;
#endif
}
bool is_superh() {
#if defined(__sh__)
return true;
#else
return false;
#endif
}
using i32=std::int32_t;
using i64=std::int64_t;
using u8=std::uint8_t;
using u16=std::uint16_t;
using u32=std::uint32_t;
using u64=std::uint64_t;
using usize=std::size_t;
using f64=double;
using std::string;
const u32 STACK_DEPTH=4096;
f64 hex2f(const char* str) {
f64 ret=0;
for(; *str; ++str) {
if ('0'<=*str && *str<='9') {
ret=ret*16+(*str-'0');
} else if ('a'<=*str && *str<='f') {
ret=ret*16+(*str-'a'+10);
} else if ('A'<=*str && *str<='F') {
ret=ret*16+(*str-'A'+10);
} else {
return nan("");
}
}
return ret;
}
f64 oct2f(const char* str) {
f64 ret=0;
while('0'<=*str && *str<'8') {
ret=ret*8+(*str++-'0');
}
if (*str) {
return nan("");
}
return ret;
}
// we have the same reason not using atof here
// just as andy's interpreter does.
// it is not platform independent, and may have strange output.
// so we write a new function here to convert str to number manually.
// but this also makes 0.1+0.2==0.3,
// not another result that you may get in other languages.
f64 dec2f(const char* str) {
f64 ret=0,negative=1,num_pow=0;
while('0'<=*str && *str<='9') {
ret=ret*10+(*str++-'0');
}
if (!*str) {
return ret;
}
if (*str=='.') {
if (!*++str) {
return nan("");
}
num_pow=0.1;
while('0'<=*str && *str<='9') {
ret+=num_pow*(*str++-'0');
num_pow*=0.1;
}
if (!*str) {
return ret;
}
}
if (*str!='e' && *str!='E') {
return nan("");
}
if (!*++str) {
return nan("");
}
if (*str=='-' || *str=='+') {
negative=(*str++=='-'? -1:1);
}
if (!*str) {
return nan("");
}
num_pow=0;
while('0'<=*str && *str<='9') {
num_pow=num_pow*10+(*str++-'0');
}
if (*str) {
return nan("");
}
return ret*std::pow(10,negative*num_pow);
}
f64 str2num(const char* str) {
bool negative=false;
f64 res=0;
if (*str=='-' || *str=='+') {
negative=(*str++=='-');
}
if (!*str) {
return nan("");
}
if (str[0]=='0' && str[1]=='x') {
res=hex2f(str+2);
} else if (str[0]=='0' && str[1]=='o') {
res=oct2f(str+2);
} else {
res=dec2f(str);
}
return negative?-res:res;
}
i32 utf8_hdchk(const char head) {
// RFC-2279 but now we use RFC-3629 so nbytes is less than 4
const u8 c=(u8)head;
if ((c>>5)==0x06) { // 110x xxxx (10xx xxxx)^1
return 1;
}
if ((c>>4)==0x0e) { // 1110 xxxx (10xx xxxx)^2
return 2;
}
if ((c>>3)==0x1e) { // 1111 0xxx (10xx xxxx)^3
return 3;
}
return 0;
}
string chrhex(const char c) {
const char hextbl[]="0123456789abcdef";
return {hextbl[(c&0xf0)>>4],hextbl[c&0x0f]};
}
string rawstr(const string& str, const usize maxlen=0) {
string ret("");
for(auto i:str) {
// windows doesn't output unicode normally, so we output the hex
if (is_windows() && i<=0) {
ret+="\\x"+chrhex(i);
continue;
}
switch(i) {
case '\0': ret+="\\0"; break;
case '\a': ret+="\\a"; break;
case '\b': ret+="\\b"; break;
case '\t': ret+="\\t"; break;
case '\n': ret+="\\n"; break;
case '\v': ret+="\\v"; break;
case '\f': ret+="\\f"; break;
case '\r': ret+="\\r"; break;
case '\033':ret+="\\e"; break;
case '\"': ret+="\\\"";break;
case '\'': ret+="\\\'";break;
case '\\': ret+="\\\\";break;
default: ret+=i; break;
}
}
if (maxlen && ret.length()>maxlen) {
ret=ret.substr(0,maxlen)+"...";
}
return ret;
}
#include "nasal_gc.h" // declarations of var and nasal_gc

View File

@ -1,301 +0,0 @@
#pragma once
#include <vector>
#include <cstring>
#include "nasal.h"
#include "nasal_err.h"
enum ast_node:u32 {
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_bool, // bools
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_params, // 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_btandeq, // &=
ast_btoreq, // |=
ast_btxoreq, // ^=
ast_cmpeq, // ==
ast_neq, // !=
ast_less, // <
ast_leq, // <=
ast_grt, // >
ast_geq, // >=
ast_add, // +
ast_sub, // -
ast_mult, // *
ast_div, // /
ast_link, // ~
ast_neg, // unary -
ast_lnot, // unary !
ast_bnot, // unary ~ bitwise not
ast_bitor, // bitwise or
ast_bitxor, // bitwise xor
ast_bitand, // bitwise and
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_cond, // 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_tuple, // tuple, only used in multiple assignment
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",
"AbstractSyntaxTreeRoot",
"CodeBlock",
"FileIndex",
"NilLiteral",
"NumberLiteral",
"StringLiteral",
"Identifier",
"BoolLiteral",
"Function",
"HashMap",
"Vector",
"HashMapPair",
"IdentifierCall",
"HashMapCall",
"VectorCall",
"FunctionCall",
"SubVector",
"ParameterList",
"DefaultParameter",
"DynamicParameter",
"And",
"Or",
"Equal",
"AddEqual",
"SubEqual",
"MultEqual",
"DivEqual",
"LinkEqual",
"BitwiseAndEqual",
"BitwiseOrEqual",
"BitwiseXorEqual",
"CompareEqual",
"NotEqual",
"Less",
"LessOrEqual",
"Great",
"GreatOrEqual",
"Add",
"Sub",
"Mult",
"Div",
"Link",
"Negative",
"LogicalNot",
"BitwiseNot",
"BitwiseOr",
"BitwiseXor",
"BitwiseAnd",
"Trinocular",
"ForLoop",
"ForindexLoop",
"ForeachLoop",
"WhileLoop",
"Iterator",
"Condition",
"If",
"Elsif",
"Else",
"LeftTuple",
"Tuple",
"Definition",
"MultipleAssignment",
"Continue",
"Break",
"Return"
};
class ast {
public:
ast(const ast&) = delete;
ast& operator=(const ast&) = delete;
ast(ast&&) = default;
ast& operator=(ast&&) = default;
private:
void print(u32, bool, std::vector<string>&) const;
private:
span loc;
u32 nd_type;
f64 nd_num;
string nd_str;
std::vector<ast> nd_child;
public:
ast(const span& s, const u32 t)
: loc(s), nd_type(t), nd_num(0), nd_str("") {}
public:
void dump() const;
void clear();
public:
ast& operator[](usize n) {return nd_child[n];}
const ast& operator[](usize n) const {return nd_child[n];}
usize size() const {return nd_child.size();}
public:
void add(ast&& node) {nd_child.push_back(std::move(node));}
void set_begin(const u32, const u32);
void set_end(const u32, const u32);
void set_type(const u32 t) {nd_type=t;}
void set_str(const string& s) {nd_str=s;}
void set_num(const f64 n) {nd_num=n;}
public:
u32 line() const {return loc.end_line;}
u32 type() const {return nd_type;}
f64 num() const {return nd_num;}
const string& str() const {return nd_str;}
const string& file() const {return loc.file;}
const span& location() const {return loc;}
const std::vector<ast>& child() const {return nd_child;}
std::vector<ast>& child() {return nd_child;}
void update_span();
void update_span(const span&);
};
void ast::set_begin(const u32 l, const u32 c) {
loc.begin_line=l;
loc.begin_column=c;
}
void ast::set_end(const u32 l, const u32 c) {
loc.end_line=l;
loc.end_column=c;
}
void ast::clear() {
loc={0, 0, 0, 0, ""};
nd_num=0;
nd_str.clear();
nd_type=ast_null;
nd_child.clear();
}
void ast::dump() const{
std::vector<string> tmp;
print(0, false, tmp);
}
void ast::print(u32 depth, bool last, std::vector<string>& indent) const{
// output the indentation first
for(auto& i:indent) {
std::cout<<i;
}
// output ast node name
std::cout<<ast_name[nd_type];
// output string literal and number
if (nd_type==ast_str ||
nd_type==ast_id ||
nd_type==ast_bool ||
nd_type==ast_default ||
nd_type==ast_dynamic ||
nd_type==ast_callh) {
std::cout<<":"<<rawstr(nd_str);
} else if (nd_type==ast_num || nd_type==ast_file) {
std::cout<<":"<<nd_num;
}
// okay, we must know that begin_column starts from index 0
std::cout<<" --> "<<loc.file<<":"<<loc.begin_line<<":"<<loc.begin_column+1<<"\n";
// output tree structure
if (last && depth) {
indent.back()=" ";
} else if (!last && depth) {
indent.back()=is_windows()? "| ":"";
}
for(u32 i=0; i<nd_child.size(); ++i) {
if (is_windows()) {
indent.push_back(i==nd_child.size()-1?"+-":"|-");
} else {
indent.push_back(i==nd_child.size()-1?"└─":"├─");
}
nd_child[i].print(depth+1,i==nd_child.size()-1,indent);
indent.pop_back();
}
}
void ast::update_span() {
if (!nd_child.size()) {
return;
}
for(const auto& i:nd_child) {
if (loc.begin_line>i.loc.begin_line) {
loc.begin_line=i.loc.begin_line;
loc.begin_column=i.loc.begin_column;
} else if (loc.begin_line==i.loc.begin_line && loc.begin_column>i.loc.begin_column) {
loc.begin_column=i.loc.begin_column;
}
if (loc.end_line<i.loc.end_line) {
loc.end_line=i.loc.end_line;
loc.end_column=i.loc.end_column;
} else if (loc.end_line==i.loc.end_line && loc.end_column<i.loc.end_column) {
loc.end_column=i.loc.end_column;
}
loc.file=i.loc.file;
}
}
void ast::update_span(const span& tloc) {
update_span();
if (loc.begin_line>tloc.begin_line) {
loc.begin_line=tloc.begin_line;
loc.begin_column=tloc.begin_column;
} else if (loc.begin_line==tloc.begin_line && loc.begin_column>tloc.begin_column) {
loc.begin_column=tloc.begin_column;
}
if (loc.end_line<tloc.end_line) {
loc.end_line=tloc.end_line;
loc.end_column=tloc.end_column;
} else if (loc.end_line==tloc.end_line && loc.end_column<tloc.end_column) {
loc.end_column=tloc.end_column;
}
loc.file=tloc.file;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,321 +0,0 @@
#pragma once
#include "nasal_import.h"
#include "nasal_err.h"
#include "nasal_opcode.h"
#include "nasal_vm.h"
#include <cstring>
#include <algorithm>
#include <unordered_map>
class dbg:public vm {
private:
enum class dbg_cmd {
cmd_error,
cmd_help,
cmd_backtrace,
cmd_continue,
cmd_list_file,
cmd_global,
cmd_local,
cmd_upval,
cmd_register,
cmd_show_all,
cmd_next,
cmd_break_point,
cmd_exit
};
private:
const std::unordered_map<string, dbg_cmd> command_table = {
{"h", dbg_cmd::cmd_help},
{"help", dbg_cmd::cmd_help},
{"bt", dbg_cmd::cmd_backtrace},
{"backtrace", dbg_cmd::cmd_backtrace},
{"c", dbg_cmd::cmd_continue},
{"continue", dbg_cmd::cmd_continue},
{"f", dbg_cmd::cmd_list_file},
{"file", dbg_cmd::cmd_list_file},
{"g", dbg_cmd::cmd_global},
{"global", dbg_cmd::cmd_global},
{"l", dbg_cmd::cmd_local},
{"local", dbg_cmd::cmd_local},
{"u", dbg_cmd::cmd_upval},
{"upval", dbg_cmd::cmd_upval},
{"r", dbg_cmd::cmd_register},
{"register", dbg_cmd::cmd_register},
{"a", dbg_cmd::cmd_show_all},
{"all", dbg_cmd::cmd_show_all},
{"n", dbg_cmd::cmd_next},
{"next", dbg_cmd::cmd_next},
{"bk", dbg_cmd::cmd_break_point},
{"break", dbg_cmd::cmd_break_point},
{"q", dbg_cmd::cmd_exit},
{"exit", dbg_cmd::cmd_exit}
};
dbg_cmd get_cmd_type(const string& cmd) const {
return command_table.count(cmd)?
command_table.at(cmd):dbg_cmd::cmd_error;
}
private:
bool next;
usize fsize;
u16 bk_fidx;
u32 bk_line;
error& src;
std::vector<string> parse(const string&);
u16 file_index(const string&) const;
void err();
void help();
void list_file() const;
void call_sort(const u64*) const;
void step_info();
void interact();
public:
dbg(error& err):
next(false), fsize(0),
bk_fidx(0), bk_line(0),
src(err) {}
void run(
const codegen&,
const linker&,
const std::vector<string>&
);
};
std::vector<string> dbg::parse(const string& cmd) {
std::vector<string> res;
usize last=0;
usize pos=cmd.find(" ", 0);
while(pos!=string::npos) {
if (pos>last) {
res.push_back(cmd.substr(last, pos-last));
}
last=pos+1;
pos=cmd.find(" ", last);
}
if (last<cmd.length()) {
res.push_back(cmd.substr(last));
}
return res;
}
u16 dbg::file_index(const string& filename) const {
for(u16 i=0;i<fsize;++i) {
if (filename==files[i]) {
return i;
}
}
return 65535;
}
void dbg::err() {
std::cerr
<<"incorrect command\n"
<<"input \'h\' to get help\n";
}
void dbg::help() {
std::clog
<<"<option>\n"
<<" h, help | get help\n"
<<" bt, backtrace | get function call trace\n"
<<" c, continue | run program until break point or exit\n"
<<" f, file | see all the compiled files\n"
<<" g, global | see global values\n"
<<" l, local | see local values\n"
<<" u, upval | see upvalue\n"
<<" r, register | show vm register detail\n"
<<" a, all | show global,local and upvalue\n"
<<" n, next | execute next bytecode\n"
<<" q, exit | exit debugger\n"
<<"<option> <filename> <line>\n"
<<" bk, break | set break point\n";
}
void dbg::list_file() const {
for(usize i=0; i<fsize; ++i) {
std::clog<<"["<<i<<"] "<<files[i]<<"\n";
}
}
void dbg::call_sort(const u64* arr) const {
typedef std::pair<u32,u64> op;
std::vector<op> opcall;
u64 total=0;
for(u32 i=0;i<op_ret+1;++i) {
total+=arr[i];
opcall.push_back({i, arr[i]});
}
std::sort(opcall.begin(), opcall.end(),
[](const op& a, const op& b) {return a.second>b.second;}
);
std::clog<<"\noperands call info (<1% ignored)\n";
for(auto& i:opcall) {
u64 rate=i.second*100/total;
if (!rate) {
break;
}
std::clog<<" "<<opname[i.first]<<" : "<<i.second<<" ("<<rate<<"%)\n";
}
std::clog<<" total : "<<total<<'\n';
}
void dbg::step_info() {
u32 line=bytecode[ctx.pc].line==0?0:bytecode[ctx.pc].line-1;
u32 begin=(line>>3)==0?0:((line>>3)<<3);
u32 end=(1+(line>>3))<<3;
src.load(files[bytecode[ctx.pc].fidx]);
std::clog<<"\nsource code:\n";
for(u32 i=begin;i<end && i<src.size();++i) {
std::clog<<(i==line?back_white:reset)<<(i==line?"--> ":" ")<<src[i]<<reset<<"\n";
}
begin=(ctx.pc>>3)==0?0:((ctx.pc>>3)<<3);
end=(1+(ctx.pc>>3))<<3;
codestream::set(cnum, cstr, files);
std::clog<<"next bytecode:\n";
for(u32 i=begin;i<end && bytecode[i].op!=op_exit;++i) {
std::clog
<<(i==ctx.pc?back_white:reset)
<<(i==ctx.pc?"--> ":" ")
<<codestream(bytecode[i], i)
<<reset<<"\n";
}
stackinfo(10);
}
void dbg::interact() {
// special operand, end execution
if (bytecode[ctx.pc].op==op_exit) {
return;
}
if ((bytecode[ctx.pc].fidx!=bk_fidx ||
bytecode[ctx.pc].line!=bk_line) && // break point
!next) {// next step
return;
}
next=false;
string cmd;
step_info();
while(true) {
std::clog<<">> ";
std::getline(std::cin, cmd);
auto res=parse(cmd);
if (res.size()==0) {
step_info();
} else if (res.size()==1) {
switch(get_cmd_type(res[0])) {
case dbg_cmd::cmd_help: help(); break;
case dbg_cmd::cmd_backtrace: traceback(); break;
case dbg_cmd::cmd_continue: return;
case dbg_cmd::cmd_list_file: list_file(); break;
case dbg_cmd::cmd_global: gstate(); break;
case dbg_cmd::cmd_local: lstate(); break;
case dbg_cmd::cmd_upval: ustate(); break;
case dbg_cmd::cmd_register: reginfo(); break;
case dbg_cmd::cmd_show_all: detail(); break;
case dbg_cmd::cmd_next: next=true; return;
case dbg_cmd::cmd_exit: std::exit(0);
default: err(); break;
}
} else if (res.size()==3 &&
get_cmd_type(res[0])==dbg_cmd::cmd_break_point) {
bk_fidx=file_index(res[1]);
if (bk_fidx==65535) {
std::clog<<"cannot find file named `"<<res[1]<<"`\n";
continue;
}
i32 tmp=atoi(res[2].c_str());
if (tmp<=0) {
std::clog<<"incorrect line number `"<<res[2]<<"`\n";
} else {
bk_line=tmp;
}
} else {
err();
}
}
}
void dbg::run(
const codegen& gen,
const linker& linker,
const std::vector<string>& argv) {
verbose=true;
fsize=linker.filelist().size();
init(gen.strs(), gen.nums(), gen.codes(), linker.filelist(), argv);
u64 count[op_ret+1]={0};
typedef void (dbg::*nafunc)();
const nafunc oprs[]={
nullptr, &dbg::o_intg,
&dbg::o_intl, &dbg::o_loadg,
&dbg::o_loadl, &dbg::o_loadu,
&dbg::o_pnum, &dbg::o_pnil,
&dbg::o_pstr, &dbg::o_newv,
&dbg::o_newh, &dbg::o_newf,
&dbg::o_happ, &dbg::o_para,
&dbg::o_deft, &dbg::o_dyn,
&dbg::o_lnot, &dbg::o_usub,
&dbg::o_bnot, &dbg::o_btor,
&dbg::o_btxor, &dbg::o_btand,
&dbg::o_add, &dbg::o_sub,
&dbg::o_mul, &dbg::o_div,
&dbg::o_lnk, &dbg::o_addc,
&dbg::o_subc, &dbg::o_mulc,
&dbg::o_divc, &dbg::o_lnkc,
&dbg::o_addeq, &dbg::o_subeq,
&dbg::o_muleq, &dbg::o_diveq,
&dbg::o_lnkeq, &dbg::o_bandeq,
&dbg::o_boreq, &dbg::o_bxoreq,
&dbg::o_addeqc, &dbg::o_subeqc,
&dbg::o_muleqc, &dbg::o_diveqc,
&dbg::o_lnkeqc, &dbg::o_addecp,
&dbg::o_subecp, &dbg::o_mulecp,
&dbg::o_divecp, &dbg::o_lnkecp,
&dbg::o_meq, &dbg::o_eq,
&dbg::o_neq, &dbg::o_less,
&dbg::o_leq, &dbg::o_grt,
&dbg::o_geq, &dbg::o_lessc,
&dbg::o_leqc, &dbg::o_grtc,
&dbg::o_geqc, &dbg::o_pop,
&dbg::o_jmp, &dbg::o_jt,
&dbg::o_jf, &dbg::o_cnt,
&dbg::o_findex, &dbg::o_feach,
&dbg::o_callg, &dbg::o_calll,
&dbg::o_upval, &dbg::o_callv,
&dbg::o_callvi, &dbg::o_callh,
&dbg::o_callfv, &dbg::o_callfh,
&dbg::o_callb, &dbg::o_slcbeg,
&dbg::o_slcend, &dbg::o_slc,
&dbg::o_slc2, &dbg::o_mcallg,
&dbg::o_mcalll, &dbg::o_mupval,
&dbg::o_mcallv, &dbg::o_mcallh,
&dbg::o_ret
};
std::vector<u32> code;
for(auto& i:gen.codes()) {
code.push_back(i.op);
imm.push_back(i.num);
}
while(oprs[code[ctx.pc]]) {
interact();
++count[code[ctx.pc]];
(this->*oprs[code[ctx.pc]])();
if (ctx.top>=ctx.canary) {
die("stack overflow");
}
++ctx.pc;
}
call_sort(count);
ngc.info();
ngc.clear();
imm.clear();
return;
}

View File

@ -1,228 +0,0 @@
#pragma once
#include <iostream>
#include <fstream>
#include <sstream> // MSVC need this to use std::getline
#include <cstring>
#include <vector>
#include "nasal.h"
struct span {
u32 begin_line;
u32 begin_column;
u32 end_line;
u32 end_column;
string file;
};
#ifdef _WIN32
#include <windows.h> // use SetConsoleTextAttribute
struct for_reset {
CONSOLE_SCREEN_BUFFER_INFO scr;
for_reset() {
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &scr);
}
} reset_ter_color;
#endif
std::ostream& back_white(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0xf0);
#else
s<<"\033[7m";
#endif
return s;
}
std::ostream& red(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0c);
#else
s<<"\033[91;1m";
#endif
return s;
}
std::ostream& cyan(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x03);
#else
s<<"\033[36;1m";
#endif
return s;
}
std::ostream& orange(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0e);
#else
s<<"\033[93;1m";
#endif
return s;
}
std::ostream& white(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0f);
#else
s<<"\033[0m\033[1m";
#endif
return s;
}
std::ostream& reset(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), reset_ter_color.scr.wAttributes);
#else
s<<"\033[0m";
#endif
return s;
}
class flstream {
protected:
string file;
std::vector<string> res;
public:
flstream():file("") {}
void load(const string&);
const string& operator[](usize n) const {return res[n];}
const string& name() const {return file;}
usize size() const {return res.size();}
};
class error:public flstream {
private:
u32 cnt; // counter for errors
string identation(usize len) {
return string(len,' ');
}
string leftpad(u32 num, usize len) {
string tmp=std::to_string(num);
while(tmp.length()<len) {
tmp=" "+tmp;
}
return tmp;
}
public:
error():cnt(0) {}
void fatal(const string&, const string&);
void err(const string&, const string&);
void err(const string&, const span&, const string&);
void chkerr() const {
if (cnt) {
std::exit(1);
}
}
};
void flstream::load(const string& f) {
if (file==f) { // don't need to load a loaded file
return;
} else {
file=f;
}
res.clear();
std::ifstream in(f, std::ios::binary);
if (in.fail()) {
std::cerr<<red<<"src: "<<reset<<"cannot open <"<<f<<">\n";
std::exit(1);
}
while(!in.eof()) {
string line;
std::getline(in, line);
res.push_back(line);
}
}
void error::fatal(const string& stage, const string& info) {
std::cerr<<red<<stage<<": "<<white<<info<<reset<<"\n";
if (file.length()) {
std::cerr<<cyan<<" --> "<<red<<file<<reset<<"\n\n";
} else {
std::cerr<<reset<<"\n";
}
std::exit(1);
}
void error::err(const string& stage, const string& info) {
++cnt;
std::cerr<<red<<stage<<": "<<white<<info<<reset<<"\n";
if (file.length()) {
std::cerr<<cyan<<" --> "<<red<<file<<reset<<"\n\n";
} else {
std::cerr<<reset<<"\n";
}
}
void error::err(const string& stage, const span& loc, const string& info) {
// load error occurred file into string lines
load(loc.file);
++cnt;
std::cerr
<<red<<stage<<": "<<white<<info<<reset<<"\n"<<cyan<<" --> "
<<red<<loc.file<<":"<<loc.begin_line<<":"<<loc.begin_column+1<<reset<<"\n";
const usize maxlen=std::to_string(loc.end_line).length();
const string iden=identation(maxlen);
for(u32 line=loc.begin_line; line<=loc.end_line; ++line) {
if (!line) {
continue;
}
if (loc.begin_line<line && line<loc.end_line) {
if (line==loc.begin_line+1) {
std::cerr<<cyan<<iden<<" | "<<reset<<"...\n"<<cyan<<iden<<" | "<<reset<<"\n";
}
continue;
}
// if this line has nothing, skip
if (!res[line-1].length() && line!=loc.end_line) {
continue;
}
const string& code=res[line-1];
std::cerr<<cyan<<leftpad(line, maxlen)<<" | "<<reset<<code<<"\n";
// output underline
std::cerr<<cyan<<iden<<" | "<<reset;
if (loc.begin_line==loc.end_line) {
for(u32 i=0; i<loc.begin_column; ++i) {
std::cerr<<char(" \t"[code[i]=='\t']);
}
for(u32 i=loc.begin_column ;i<loc.end_column; ++i) {
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^")<<reset;
}
} else if (line==loc.begin_line) {
for(u32 i=0; i<loc.begin_column; ++i) {
std::cerr<<char(" \t"[code[i]=='\t']);
}
for(u32 i=loc.begin_column; i<code.size(); ++i) {
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^")<<reset;
}
} else if (loc.begin_line<line && line<loc.end_line) {
for(u32 i=0; i<code.size(); ++i) {
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^");
}
} else {
for(u32 i=0; i<loc.end_column; ++i) {
std::cerr<<red<<(code[i]=='\t'?"^^^^":"^");
}
}
if (line==loc.end_line) {
std::cerr<<reset;
} else {
std::cerr<<reset<<"\n";
}
}
std::cerr<<"\n\n";
}

View File

@ -1,928 +0,0 @@
#pragma once
// avoid MSVC warnings
#ifdef _MSC_VER
#pragma warning (disable:4244)
#pragma warning (disable:4267)
#pragma warning (disable:4102)
#endif
#ifndef _MSC_VER
#include <unistd.h>
#include <dirent.h>
#else
#include <io.h>
#include <direct.h>
#endif
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif
#include <iomanip>
#include <vector>
#include <unordered_map>
#include <chrono>
#include <algorithm>
#include "nasal.h"
#include "nasal_err.h"
enum vm_type:u8 {
/* none-gc object */
vm_none=0,
vm_cnt,
vm_addr,
vm_ret,
vm_nil,
vm_num,
/* gc object */
vm_str,
vm_vec,
vm_hash,
vm_func,
vm_upval,
vm_obj,
vm_co
};
const u32 gc_type_size=vm_co-vm_str+1;
enum class coroutine_status:u32 {
suspended,
running,
dead
};
enum class gc_status:u8 {
uncollected=0,
collected,
found
};
struct nas_vec; // vector
struct nas_hash; // hashmap(dict)
struct nas_func; // function(lambda)
struct nas_upval; // upvalue
struct nas_ghost; // objects
struct nas_co; // coroutine
struct nas_val; // nas_val includes gc-managed types
struct var {
public:
u8 type;
union {
u32 ret;
i64 cnt;
f64 num;
var* addr;
nas_val* gcobj;
} val;
private:
var(u8 t, u32 pc) {type=t;val.ret=pc;}
var(u8 t, i64 ct) {type=t;val.cnt=ct;}
var(u8 t, f64 n) {type=t;val.num=n;}
var(u8 t, var* p) {type=t;val.addr=p;}
var(u8 t, nas_val* p) {type=t;val.gcobj=p;}
public:
var() = default;
var(const var&) = default;
bool operator==(const var& nr) const {return type==nr.type && val.gcobj==nr.val.gcobj;}
bool operator!=(const var& nr) const {return type!=nr.type || val.gcobj!=nr.val.gcobj;}
friend std::ostream& operator<<(std::ostream&, var&);
// number and string can be translated to each other
f64 tonum();
string tostr();
bool objchk(usize);
// create new var object
static var none();
static var nil();
static var ret(u32);
static var cnt(i64);
static var num(f64);
static var gcobj(nas_val*);
static var addr(var*);
// get content
var* addr();
u32 ret ();
i64& cnt ();
f64 num ();
string& str ();
nas_vec& vec ();
nas_hash& hash();
nas_func& func();
nas_upval& upval();
nas_ghost& obj ();
nas_co& co ();
};
struct nas_vec {
std::vector<var> elems;
// mark if this is printed, avoid stackoverflow
bool printed;
nas_vec():printed(false) {}
usize size() const {return elems.size();}
var get_val(const i32);
var* get_mem(const i32);
};
struct nas_hash {
std::unordered_map<string,var> elems;
// mark if this is printed, avoid stackoverflow
bool printed;
nas_hash():printed(false) {}
usize size() const {return elems.size();}
var get_val(const string&);
var* get_mem(const string&);
};
struct nas_func {
i32 dpara; // dynamic parameter name index in hash.
u32 entry; // pc will set to entry-1 to call this function
u32 psize; // used to load default parameters to a new function
u32 lsize; // used to expand memory space for local values on stack
std::vector<var> local; // local scope with default value(var)
std::vector<var> upval; // closure
std::unordered_map<u32,u32> keys; // parameter table, u32 begins from 1
nas_func(): dpara(-1), entry(0), psize(0), lsize(0) {}
void clear();
};
struct nas_upval {
public:
/* on stack, use these variables */
bool onstk;
u32 size;
var* stk;
/* not on stack, use this */
std::vector<var> elems;
public:
nas_upval(): onstk(true), size(0), stk(nullptr) {}
var& operator[](usize n) {
return onstk? stk[n]:elems[n];
}
void clear() {
onstk=true;
elems.clear();
size=0;
}
};
void filehandle_destructor(void* ptr) {
if ((FILE*)ptr==stdin) {
return;
}
fclose((FILE*)ptr);
}
void dir_entry_destructor(void* ptr) {
#ifndef _MSC_VER
closedir((DIR*)ptr);
#else
FindClose(ptr);
#endif
}
void dylib_destructor(void* ptr) {
#ifdef _WIN32
FreeLibrary((HMODULE)ptr);
#else
dlclose(ptr);
#endif
}
void func_addr_destructor(void* ptr) {}
struct ghost_register_table {
private:
using dtor=void (*)(void*);
private:
std::unordered_map<string,usize> mapper;
std::vector<string> ghost_name;
std::vector<dtor> destructors;
public:
// reserved ghost type only for native functions
usize ghost_file;
usize ghost_dir;
usize ghost_dylib;
usize ghost_faddr;
public:
ghost_register_table() {
ghost_file=register_ghost_type("file", filehandle_destructor);
ghost_dir=register_ghost_type("dir", dir_entry_destructor);
ghost_dylib=register_ghost_type("dylib", dylib_destructor);
ghost_faddr=register_ghost_type("faddr", func_addr_destructor);
}
bool exists(const string& name) const {
return mapper.count(name);
}
usize get_ghost_type_index(const string& name) const {
return mapper.at(name);
}
const string& get_ghost_name(usize index) const {
return ghost_name.at(index);
}
usize register_ghost_type(const std::string& name, dtor ptr) {
if (mapper.count(name)) {
std::cerr<<"nasal_gc.h: ghost_register_table::register_ghost_type: ";
std::cerr<<"ghost type \""<<name<<"\" already exists.\n";
std::exit(1);
}
auto res=destructors.size();
mapper[name]=res;
ghost_name.push_back(name);
destructors.push_back(ptr);
return res;
}
dtor destructor(usize index) {
return destructors.at(index);
}
};
struct nas_ghost {
public:
usize type;
void* ptr;
private:
ghost_register_table* ghost_type_table;
public:
nas_ghost(): type(0), ptr(nullptr), ghost_type_table(nullptr) {}
~nas_ghost() {clear();}
void set(usize, void*, ghost_register_table*);
void clear();
public:
friend std::ostream& operator<<(std::ostream& out, nas_ghost& ghost) {
out<<"<object "<<ghost.ghost_type_table->get_ghost_name(ghost.type);
out<<" at 0x"<<std::hex<<(u64)ghost.ptr<<std::dec<<">";
return out;
}
const std::string& get_ghost_name() const {
return ghost_type_table->get_ghost_name(type);
}
};
struct context {
u32 pc;
var* localr;
var* memr;
var funcr;
var upvalr;
var* canary;
var* stack;
var* top;
};
struct nas_co {
var stack[STACK_DEPTH];
context ctx;
coroutine_status status;
nas_co() {clear();}
void clear();
};
struct nas_val {
gc_status mark;
u8 type; // value type
u8 unmut; // used to mark if a string is unmutable
union {
string* str;
nas_vec* vec;
nas_hash* hash;
nas_func* func;
nas_upval* upval;
nas_ghost* obj;
nas_co* co;
} ptr;
nas_val(u8);
~nas_val();
void clear();
};
var nas_vec::get_val(const i32 n) {
i32 size=elems.size();
if (n<-size || n>=size) {
return var::none();
}
return elems[n>=0?n:n+size];
}
var* nas_vec::get_mem(const i32 n) {
i32 size=elems.size();
if (n<-size || n>=size) {
return nullptr;
}
return &elems[n>=0?n:n+size];
}
std::ostream& operator<<(std::ostream& out, nas_vec& vec) {
if (!vec.elems.size() || vec.printed) {
out<<(vec.elems.size()?"[..]":"[]");
return out;
}
vec.printed=true;
usize iter=0,size=vec.elems.size();
out<<'[';
for(auto& i:vec.elems) {
out<<i<<",]"[(++iter)==size];
}
vec.printed=false;
return out;
}
var nas_hash::get_val(const string& key) {
if (elems.count(key)) {
return elems.at(key);
} else if (!elems.count("parents")) {
return var::none();
}
var ret=var::none();
var val=elems.at("parents");
if (val.type!=vm_vec) {
return ret;
}
for(auto& i:val.vec().elems) {
if (i.type==vm_hash) {
ret=i.hash().get_val(key);
}
if (ret.type!=vm_none) {
return ret;
}
}
return ret;
}
var* nas_hash::get_mem(const string& key) {
if (elems.count(key)) {
return &elems.at(key);
} else if (!elems.count("parents")) {
return nullptr;
}
var* addr=nullptr;
var val=elems.at("parents");
if (val.type!=vm_vec) {
return addr;
}
for(auto& i:val.vec().elems) {
if (i.type==vm_hash) {
addr=i.hash().get_mem(key);
}
if (addr) {
return addr;
}
}
return addr;
}
std::ostream& operator<<(std::ostream& out, nas_hash& hash) {
if (!hash.elems.size() || hash.printed) {
out<<(hash.elems.size()?"{..}":"{}");
return out;
}
hash.printed=true;
usize iter=0,size=hash.elems.size();
out<<'{';
for(auto& i:hash.elems) {
out<<i.first<<':'<<i.second<<",}"[(++iter)==size];
}
hash.printed=false;
return out;
}
void nas_func::clear() {
dpara=-1;
local.clear();
upval.clear();
keys.clear();
}
void nas_ghost::set(usize t, void* p, ghost_register_table* table) {
type=t;
ptr=p;
ghost_type_table=table;
}
void nas_ghost::clear() {
if (!ptr) {
return;
}
ghost_type_table->destructor(type)(ptr);
ptr=nullptr;
}
void nas_co::clear() {
for(u32 i=0;i<STACK_DEPTH;++i) {
stack[i]=var::nil();
}
ctx.pc=0;
ctx.localr=nullptr;
ctx.memr=nullptr;
ctx.canary=stack+STACK_DEPTH-1;
ctx.top=stack;
ctx.funcr=var::nil();
ctx.upvalr=var::nil();
ctx.stack=stack;
status=coroutine_status::suspended;
}
nas_val::nas_val(u8 val_type) {
mark=gc_status::collected;
type=val_type;
unmut=0;
switch(val_type) {
case vm_str: ptr.str=new string; break;
case vm_vec: ptr.vec=new nas_vec; break;
case vm_hash: ptr.hash=new nas_hash; break;
case vm_func: ptr.func=new nas_func; break;
case vm_upval:ptr.upval=new nas_upval;break;
case vm_obj: ptr.obj=new nas_ghost; break;
case vm_co: ptr.co=new nas_co; break;
}
}
nas_val::~nas_val() {
switch(type) {
case vm_str: delete ptr.str; break;
case vm_vec: delete ptr.vec; break;
case vm_hash: delete ptr.hash; break;
case vm_func: delete ptr.func; break;
case vm_upval:delete ptr.upval;break;
case vm_obj: delete ptr.obj; break;
case vm_co: delete ptr.co; break;
}
type=vm_nil;
}
void nas_val::clear() {
switch(type) {
case vm_str: ptr.str->clear(); break;
case vm_vec: ptr.vec->elems.clear(); break;
case vm_hash: ptr.hash->elems.clear();break;
case vm_func: ptr.func->clear(); break;
case vm_upval:ptr.upval->clear(); break;
case vm_obj: ptr.obj->clear(); break;
case vm_co: ptr.co->clear(); break;
}
}
f64 var::tonum() {
return type!=vm_str? val.num:str2num(str().c_str());
}
string var::tostr() {
if (type==vm_str) {
return str();
} else if (type==vm_num) {
string tmp=std::to_string(num());
tmp.erase(tmp.find_last_not_of('0')+1, string::npos);
tmp.erase(tmp.find_last_not_of('.')+1, string::npos);
return tmp;
}
return "";
}
std::ostream& operator<<(std::ostream& out, var& ref) {
switch(ref.type) {
case vm_none: out<<"undefined"; break;
case vm_nil: out<<"nil"; break;
case vm_num: out<<ref.val.num; break;
case vm_str: out<<ref.str(); break;
case vm_vec: out<<ref.vec(); break;
case vm_hash: out<<ref.hash(); break;
case vm_func: out<<"func(..) {..}";break;
case vm_obj: out<<ref.obj(); break;
case vm_co: out<<"<coroutine>"; break;
}
return out;
}
bool var::objchk(usize obj_type) {
return type==vm_obj && obj().type==obj_type && obj().ptr;
}
var var::none() {
return {vm_none, (u32)0};
}
var var::nil() {
return {vm_nil, (u32)0};
}
var var::ret(u32 pc) {
return {vm_ret, pc};
}
var var::cnt(i64 n) {
return {vm_cnt, n};
}
var var::num(f64 n) {
return {vm_num, n};
}
var var::gcobj(nas_val* p) {
return {p->type, p};
}
var var::addr(var* p) {
return {vm_addr, p};
}
var* var::addr () {return val.addr; }
u32 var::ret () {return val.ret; }
i64& var::cnt () {return val.cnt; }
f64 var::num () {return val.num; }
string& var::str () {return *val.gcobj->ptr.str; }
nas_vec& var::vec () {return *val.gcobj->ptr.vec; }
nas_hash& var::hash () {return *val.gcobj->ptr.hash; }
nas_func& var::func () {return *val.gcobj->ptr.func; }
nas_upval& var::upval() {return *val.gcobj->ptr.upval;}
nas_ghost& var::obj () {return *val.gcobj->ptr.obj; }
nas_co& var::co () {return *val.gcobj->ptr.co; }
const var zero=var::num(0);
const var one =var::num(1);
const var nil =var::nil();
struct gc {
ghost_register_table global_ghost_type_table;
/* main context temporary storage */
context mctx;
/* runtime context */
context* rctx;
nas_co* cort=nullptr; // running coroutine
/* temporary space used in builtin/module functions */
var temp=nil;
/* constants and memory pool */
std::vector<var> strs; // reserved address for const vm_str
std::vector<var> env_argv; // command line arguments
std::vector<nas_val*> memory; // gc memory
std::vector<nas_val*> unused[gc_type_size]; // gc free list
/* heap increase size */
u32 incr[gc_type_size]={
128, // vm_str
128, // vm_vec
64, // vm_hash
128, // vm_func
256, // vm_upval
16, // vm_obj
16 // vm_co
};
/* values for analysis */
u64 size[gc_type_size];
u64 gcnt[gc_type_size];
u64 acnt[gc_type_size];
i64 worktime=0;
gc(context* _ctx): rctx(_ctx) {}
private:
/* gc functions */
void mark();
void mark_context(std::vector<var>&);
void mark_var(std::vector<var>&, var&);
inline void mark_vec(std::vector<var>&, nas_vec&);
inline void mark_hash(std::vector<var>&, nas_hash&);
inline void mark_func(std::vector<var>&, nas_func&);
inline void mark_upval(std::vector<var>&, nas_upval&);
inline void mark_co(std::vector<var>&, nas_co&);
void sweep();
public:
void extend(u8);
void init(const std::vector<string>&, const std::vector<string>&);
void clear();
void info();
var alloc(const u8);
void ctxchg(nas_co&);
void ctxreserve();
public:
var newstr(char c) {
var s=alloc(vm_str);
s.str()=c;
return s;
}
var newstr(const char* buff) {
var s=alloc(vm_str);
s.str()=buff;
return s;
}
var newstr(const string& buff) {
var s=alloc(vm_str);
s.str()=buff;
return s;
}
};
void gc::mark() {
std::vector<var> bfs;
mark_context(bfs);
while(!bfs.empty()) {
var value=bfs.back();
bfs.pop_back();
if (value.type<=vm_num ||
value.val.gcobj->mark!=gc_status::uncollected) {
continue;
}
mark_var(bfs, value);
}
}
void gc::mark_context(std::vector<var>& bfs_queue) {
// scan now running context, this context maybe related to coroutine or main
for(var* i=rctx->stack;i<=rctx->top;++i) {
bfs_queue.push_back(*i);
}
bfs_queue.push_back(rctx->funcr);
bfs_queue.push_back(rctx->upvalr);
bfs_queue.push_back(temp);
if (!cort) {
return;
}
// coroutine is running, so scan main process stack from mctx
for(var* i=mctx.stack;i<=mctx.top;++i) {
bfs_queue.push_back(*i);
}
bfs_queue.push_back(mctx.funcr);
bfs_queue.push_back(mctx.upvalr);
}
void gc::mark_var(std::vector<var>& bfs_queue, var& value) {
value.val.gcobj->mark=gc_status::found;
switch(value.type) {
case vm_vec: mark_vec(bfs_queue, value.vec()); break;
case vm_hash: mark_hash(bfs_queue, value.hash()); break;
case vm_func: mark_func(bfs_queue, value.func()); break;
case vm_upval: mark_upval(bfs_queue, value.upval()); break;
case vm_co: mark_co(bfs_queue, value.co()); break;
default: break;
}
}
void gc::mark_vec(std::vector<var>& bfs_queue, nas_vec& vec) {
for(auto& i:vec.elems) {
bfs_queue.push_back(i);
}
}
void gc::mark_hash(std::vector<var>& bfs_queue, nas_hash& hash) {
for(auto& i:hash.elems) {
bfs_queue.push_back(i.second);
}
}
void gc::mark_func(std::vector<var>& bfs_queue, nas_func& function) {
for(auto& i:function.local) {
bfs_queue.push_back(i);
}
for(auto& i:function.upval) {
bfs_queue.push_back(i);
}
}
void gc::mark_upval(std::vector<var>& bfs_queue, nas_upval& upval) {
for(auto& i:upval.elems) {
bfs_queue.push_back(i);
}
}
void gc::mark_co(std::vector<var>& bfs_queue, nas_co& co) {
bfs_queue.push_back(co.ctx.funcr);
bfs_queue.push_back(co.ctx.upvalr);
for(var* i=co.stack;i<=co.ctx.top;++i) {
bfs_queue.push_back(*i);
}
}
void gc::sweep() {
for(auto i:memory) {
if (i->mark==gc_status::uncollected) {
i->clear();
unused[i->type-vm_str].push_back(i);
i->mark=gc_status::collected;
} else if (i->mark==gc_status::found) {
i->mark=gc_status::uncollected;
}
}
}
void gc::extend(u8 type) {
const u8 index=type-vm_str;
size[index]+=incr[index];
for(u32 i=0;i<incr[index];++i) {
nas_val* tmp=new(std::nothrow) nas_val(type);
if (!tmp) {
std::cerr<<"nasal_gc.h: gc::extend: ";
std::cerr<<"failed to allocate memory\n";
std::exit(1);
}
// add to heap
memory.push_back(tmp);
unused[index].push_back(tmp);
}
incr[index]=incr[index]+incr[index]/2;
}
void gc::init(const std::vector<string>& s, const std::vector<string>& argv) {
// initialize function register
rctx->funcr=nil;
worktime=0;
// initialize counters
for(u8 i=0;i<gc_type_size;++i) {
size[i]=gcnt[i]=acnt[i]=0;
}
// coroutine pointer set to nullptr
cort=nullptr;
// init constant strings
strs.resize(s.size());
for(u32 i=0;i<strs.size();++i) {
strs[i]=var::gcobj(new nas_val(vm_str));
strs[i].val.gcobj->unmut=1;
strs[i].str()=s[i];
}
// record arguments
env_argv.resize(argv.size());
for(usize i=0;i<argv.size();++i) {
env_argv[i]=var::gcobj(new nas_val(vm_str));
env_argv[i].val.gcobj->unmut=1;
env_argv[i].str()=argv[i];
}
}
void gc::clear() {
for(auto i:memory) {
delete i;
}
memory.clear();
for(u8 i=0;i<gc_type_size;++i) {
unused[i].clear();
}
for(auto& i:strs) {
delete i.val.gcobj;
}
strs.clear();
env_argv.clear();
}
void gc::info() {
using std::left;
using std::setw;
using std::setfill;
const char* name[]={"str ","vec ","hash ","func ","upval","obj ","co "};
std::clog<<"\ngc info (gc count|alloc count|memory size)\n";
usize ident=0;
for(u8 i=0;i<gc_type_size;++i) {
#ifndef _MSC_VER
usize len=std::max({
std::to_string(gcnt[i]).length(),
std::to_string(acnt[i]).length(),
std::to_string(size[i]).length()
});
#else // VS is a piece of shit
usize len=std::to_string(gcnt[i]).length();
ident=ident<len?len:ident;
len=std::to_string(acnt[i]).length();
ident=ident<len?len:ident;
len=std::to_string(size[i]).length();
#endif
ident=ident<len?len:ident;
}
double total=0;
for(u8 i=0;i<gc_type_size;++i) {
if (gcnt[i] || acnt[i] || size[i]) {
total+=gcnt[i];
std::clog<<" "<<name[i];
std::clog<<" | "<<left<<setw(ident)<<setfill(' ')<<gcnt[i];
std::clog<<" | "<<left<<setw(ident)<<setfill(' ')<<acnt[i];
std::clog<<" | "<<left<<setw(ident)<<setfill(' ')<<size[i];
std::clog<<"\n";
}
}
double sec=worktime*1.0/1000000000; // seconds
std::clog<<" time | "<<(sec<0.1? sec*1000:sec)<<(sec<0.1? " ms\n":" s\n");
if (total) {
std::clog<<" avg | "<<sec/total*1000<<" ms\n";
}
std::clog<<"\n";
}
var gc::alloc(u8 type) {
using clk=std::chrono::high_resolution_clock;
const u8 index=type-vm_str;
++acnt[index];
if (unused[index].empty()) {
++gcnt[index];
auto begin=clk::now();
mark();
sweep();
worktime+=(clk::now()-begin).count();
}
if (unused[index].empty()) {
extend(type);
}
var ret=var::gcobj(unused[index].back());
ret.val.gcobj->mark=gc_status::uncollected;
unused[index].pop_back();
return ret;
}
void gc::ctxchg(nas_co& co) {
// store running state to main context
mctx=*rctx;
// restore coroutine context state
*rctx=co.ctx;
// set coroutine pointer
cort=&co;
// set coroutine state to running
cort->status=coroutine_status::running;
}
void gc::ctxreserve() {
// pc=0 means this coroutine is finished
cort->status=rctx->pc?
coroutine_status::suspended:
coroutine_status::dead;
// store running state to coroutine
cort->ctx=*rctx;
// restore main context state
*rctx=mctx;
// set coroutine pointer to nullptr
cort=nullptr;
}
// use to print error log and return error value
var nas_err(const string& error_function_name, const string& info) {
std::cerr<<"[vm] "<<error_function_name<<": "<<info<<"\n";
return var::none();
}
// module function type
typedef var (*module_func)(var*, usize, gc*);
// module function stores in tables with this type, end with {nullptr,nullptr}
struct module_func_info {
const char* name;
module_func fd;
};
// module function "get" type
typedef module_func_info* (*get_func_ptr)(ghost_register_table*);

View File

@ -1,226 +0,0 @@
#pragma once
#ifndef _MSC_VER
#include <unistd.h>
#else
#define _CRT_SECURE_NO_DEPRECATE 1
#define _CRT_NONSTDC_NO_DEPRECATE 1
#include <io.h>
#endif
#ifdef _MSC_VER
#define F_OK 0
#endif
#include "nasal.h"
#include "nasal_ast.h"
#include "nasal_lexer.h"
#include "nasal_parse.h"
class linker{
private:
bool show_path;
bool lib_loaded;
error& err;
std::vector<string> files;
std::vector<string> envpath;
bool imptchk(const ast&);
bool exist(const string&);
void link(ast&, ast&&);
string path(const ast&);
string findf(const string&);
ast fimpt(ast&);
ast libimpt();
ast load(ast&, u16);
public:
linker(error&);
const error& link(parse&, const string&, bool);
const std::vector<string>& filelist() const {return files;}
};
linker::linker(error& e): show_path(false), lib_loaded(false), err(e) {
char sep=is_windows()? ';':':';
string PATH=getenv("PATH");
usize last=0;
usize pos=PATH.find(sep, 0);
while(pos!=string::npos) {
string dirpath=PATH.substr(last, pos-last);
if (dirpath.length()) {
envpath.push_back(dirpath);
}
last=pos+1;
pos=PATH.find(sep, last);
}
if (last!=PATH.length()) {
envpath.push_back(PATH.substr(last));
}
}
string linker::path(const ast& node) {
if (node[1].type()==ast_callf) {
return node[1][0].str();
}
string fpath=".";
for(usize i=1;i<node.size();++i) {
fpath+=(is_windows()? "\\":"/")+node[i].str();
}
return fpath+".nas";
}
string linker::findf(const string& fname) {
// first add file name itself into the file path
std::vector<string> fpath={fname};
// generate search path from environ path
for(auto&p:envpath) {
fpath.push_back(p+(is_windows()? "\\":"/")+fname);
}
// search file
for(auto& i:fpath) {
if (access(i.c_str(), F_OK)!=-1) {
return i;
}
}
// we will find lib.nas in nasal std directory
if (fname=="lib.nas") {
return is_windows()? findf("std\\lib.nas"):findf("std/lib.nas");
}
if (!show_path) {
err.err("link", "cannot find file <"+fname+">");
return "";
}
string paths="";
for(auto& i:fpath) {
paths+=" "+i+"\n";
}
err.err("link", "cannot find file <"+fname+"> in these paths:\n"+paths);
return "";
}
bool linker::imptchk(const ast& node) {
// only these two kinds of node can be recognized as 'import':
/*
call
|_id:import
|_callh:std
|_callh:file
*/
if (node.type()==ast_call && node[0].str()=="import" && node.size()>=2 && node[1].type()==ast_callh) {
for(usize i=1;i<node.size();++i) {
if (node[i].type()!=ast_callh) {
return false;
}
}
return true;
}
/*
call
|_id:import
|_call_func
|_string:'filename'
*/
return (
node.type()==ast_call &&
node[0].str()=="import" &&
node.size()==2 &&
node[1].type()==ast_callf &&
node[1].size()==1 &&
node[1][0].type()==ast_str
);
}
bool linker::exist(const string& file) {
// avoid importing the same file
for(auto& fname:files) {
if (file==fname) {
return true;
}
}
files.push_back(file);
return false;
}
void linker::link(ast& root, ast&& add_root) {
// add children of add_root to the back of root
for(auto& i:add_root.child()) {
root.add(std::move(i));
}
}
ast linker::fimpt(ast& node) {
lexer lex(err);
parse par(err);
// get filename and set node to ast_null
string filename=path(node);
node.clear();
// avoid infinite loading loop
filename=findf(filename);
if (!filename.length() || exist(filename)) {
return {{0, 0, 0, 0, filename}, ast_root};
}
// start importing...
lex.scan(filename);
par.compile(lex);
ast tmp=std::move(par.tree());
// check if tmp has 'import'
return load(tmp, files.size()-1);
}
ast linker::libimpt() {
lexer lex(err);
parse par(err);
string filename=findf("lib.nas");
if (!filename.length()) {
return {{0, 0, 0, 0, filename}, ast_root};
}
// avoid infinite loading loop
if (exist(filename)) {
return {{0, 0, 0, 0, filename}, ast_root};
}
// start importing...
lex.scan(filename);
par.compile(lex);
ast tmp=std::move(par.tree());
// check if tmp has 'import'
return load(tmp, files.size()-1);
}
ast linker::load(ast& root, u16 fileindex) {
ast tree({0, 0, 0, 0, files[fileindex]}, ast_root);
if (!lib_loaded) {
link(tree, libimpt());
lib_loaded=true;
}
for(auto& i:root.child()) {
if (imptchk(i)) {
link(tree, fimpt(i));
} else {
break;
}
}
// add root to the back of tree
ast file_head({0, 0, 0, 0, files[fileindex]}, ast_file);
file_head.set_num(fileindex);
tree.add(std::move(file_head));
link(tree, std::move(root));
return tree;
}
const error& linker::link(parse& parse, const string& self, bool spath=false) {
show_path=spath;
// 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.tree()=load(parse.tree(), 0);
return err;
}

View File

@ -1,506 +0,0 @@
#pragma once
#ifdef _MSC_VER
#pragma warning (disable:4244)
#pragma warning (disable:4267)
#pragma warning (disable:4102)
#endif
#include <cstring>
#include <sstream>
#include <vector>
#include <unordered_map>
#include <sys/stat.h>
#include "nasal.h"
#include "nasal_err.h"
#ifdef _MSC_VER
#define S_ISREG(m) (((m)&0xF000)==0x8000)
#endif
enum class tok:u32 {
null=0, // null token (default token type)
num, // number literal
str, // string literal
id, // identifier
tktrue, // keyword true
tkfalse, // keyword false
rfor, // loop keyword for
forindex, // loop keyword forindex
foreach, // loop keyword foreach
rwhile, // loop keyword while
var, // keyword for definition
func, // keyword for definition of function
brk, // loop keyword break
cont, // loop keyword continue
ret, // function keyword return
rif, // condition expression keyword if
elsif, // condition expression keyword elsif
relse, // condition expression keyword else
tknil, // nil literal
lcurve, // (
rcurve, // )
lbracket, // [
rbracket, // ]
lbrace, // {
rbrace, // }
semi, // ;
opand, // operator and
opor, // operator or
comma, // ,
dot, // .
ellipsis, // ...
quesmark, // ?
colon, // :
add, // operator +
sub, // operator -
mult, // operator *
div, // operator /
floater, // operator ~ and binary operator ~
btand, // bitwise operator &
btor, // bitwise operator |
btxor, // bitwise operator ^
opnot, // operator !
eq, // operator =
addeq, // operator +=
subeq, // operator -=
multeq, // operator *=
diveq, // operator /=
lnkeq, // operator ~=
btandeq, // operator &=
btoreq, // operator |=
btxoreq, // operator ^=
cmpeq, // operator ==
neq, // operator !=
less, // operator <
leq, // operator <=
grt, // operator >
geq, // operator >=
eof // <eof> end of token list
};
struct token {
span loc; // location
tok type; // token type
string str; // content
token() = default;
token(const token&) = default;
};
class lexer {
private:
u32 line;
u32 column;
usize ptr;
string filename;
string res;
error& err;
std::vector<token> toks;
const std::unordered_map<string,tok> typetbl {
{"true" ,tok::tktrue },
{"false" ,tok::tkfalse },
{"for" ,tok::rfor },
{"forindex",tok::forindex},
{"foreach" ,tok::foreach },
{"while" ,tok::rwhile },
{"var" ,tok::var },
{"func" ,tok::func },
{"break" ,tok::brk },
{"continue",tok::cont },
{"return" ,tok::ret },
{"if" ,tok::rif },
{"elsif" ,tok::elsif },
{"else" ,tok::relse },
{"nil" ,tok::tknil },
{"(" ,tok::lcurve },
{")" ,tok::rcurve },
{"[" ,tok::lbracket},
{"]" ,tok::rbracket},
{"{" ,tok::lbrace },
{"}" ,tok::rbrace },
{";" ,tok::semi },
{"and" ,tok::opand },
{"or" ,tok::opor },
{"," ,tok::comma },
{"." ,tok::dot },
{"..." ,tok::ellipsis},
{"?" ,tok::quesmark},
{":" ,tok::colon },
{"+" ,tok::add },
{"-" ,tok::sub },
{"*" ,tok::mult },
{"/" ,tok::div },
{"~" ,tok::floater },
{"&" ,tok::btand },
{"|" ,tok::btor },
{"^" ,tok::btxor },
{"!" ,tok::opnot },
{"=" ,tok::eq },
{"+=" ,tok::addeq },
{"-=" ,tok::subeq },
{"*=" ,tok::multeq },
{"/=" ,tok::diveq },
{"~=" ,tok::lnkeq },
{"&=" ,tok::btandeq },
{"|=" ,tok::btoreq },
{"^=" ,tok::btxoreq },
{"==" ,tok::cmpeq },
{"!=" ,tok::neq },
{"<" ,tok::less },
{"<=" ,tok::leq },
{">" ,tok::grt },
{">=" ,tok::geq }
};
tok get_type(const string&);
bool skip(char);
bool is_id(char);
bool is_hex(char);
bool is_oct(char);
bool is_dec(char);
bool is_str(char);
bool is_single_opr(char);
bool is_calc_opr(char);
void skip_note();
void err_char();
void open(const string&);
string utf8_gen();
token id_gen();
token num_gen();
token str_gen();
token single_opr();
token dots();
token calc_opr();
public:
lexer(error& e): line(1), column(0), ptr(0), filename(""), res(""), err(e) {}
const error& scan(const string&);
const std::vector<token>& result() const {return toks;}
};
bool lexer::skip(char c) {
return c==' '||c=='\n'||c=='\t'||c=='\r'||c==0;
}
bool lexer::is_id(char c) {
return (c=='_')||('a'<=c && c<='z')||('A'<=c&&c<='Z')||(c<0);
}
bool lexer::is_hex(char c) {
return ('0'<=c&&c<='9')||('a'<=c&&c<='f')||('A'<=c && c<='F');
}
bool lexer::is_oct(char c) {
return '0'<=c&&c<='7';
}
bool lexer::is_dec(char c) {
return '0'<=c&&c<='9';
}
bool lexer::is_str(char c) {
return c=='\''||c=='\"'||c=='`';
}
bool lexer::is_single_opr(char c) {
return (
c=='('||c==')'||c=='['||c==']'||
c=='{'||c=='}'||c==','||c==';'||
c==':'||c=='?'||c=='`'||c=='@'||
c=='%'||c=='$'||c=='\\'
);
}
bool lexer::is_calc_opr(char c) {
return (
c=='='||c=='+'||c=='-'||c=='*'||
c=='!'||c=='/'||c=='<'||c=='>'||
c=='~'||c=='|'||c=='&'||c=='^'
);
}
void lexer::skip_note() {
// avoid note, after this process ptr will point to a '\n', so next loop line counter+1
while(++ptr<res.size() && res[ptr]!='\n') {}
}
void lexer::err_char() {
++column;
char c=res[ptr++];
err.err("lexer", {line, column-1, line, column, filename}, "invalid character 0x"+chrhex(c));
err.fatal("lexer", "fatal error occurred, stop");
}
void lexer::open(const string& file) {
// check file exsits and it is a regular file
struct stat buffer;
if (stat(file.c_str(), &buffer)==0 && !S_ISREG(buffer.st_mode)) {
err.err("lexer", "<"+file+"> is not a regular file");
err.chkerr();
}
// load
filename=file;
std::ifstream in(file, std::ios::binary);
if (in.fail()) {
err.err("lexer", "failed to open <"+file+">");
} else {
err.load(file);
}
std::stringstream ss;
ss<<in.rdbuf();
res=ss.str();
}
tok lexer::get_type(const string& str) {
return typetbl.count(str)?typetbl.at(str):tok::null;
}
string lexer::utf8_gen() {
string str="";
while(ptr<res.size() && res[ptr]<0) {
string tmp="";
u32 nbytes=utf8_hdchk(res[ptr]);
if (!nbytes) {
++ptr;
++column;
continue;
}
tmp+=res[ptr++];
for(u32 i=0;i<nbytes;++i,++ptr) {
if (ptr<res.size() && (res[ptr]&0xc0)==0x80) {
tmp+=res[ptr];
}
}
// utf8 character's total length is 1+nbytes
if (tmp.length()!=1+nbytes) {
++column;
string utf_info="0x"+chrhex(tmp[0]);
for(u32 i=1;i<tmp.size();++i) {
utf_info+=" 0x"+chrhex(tmp[i]);
}
err.err("lexer", {line, column-1, line, column, filename}, "invalid utf-8 <"+utf_info+">");
err.fatal("lexer", "fatal error occurred, stop");
}
str+=tmp;
column+=2; // may have some problems because not all the unicode takes 2 space
}
return str;
}
token lexer::id_gen() {
u32 begin_line=line;
u32 begin_column=column;
string str="";
while(ptr<res.size() && (is_id(res[ptr])||is_dec(res[ptr]))) {
if (res[ptr]<0) { // utf-8
str+=utf8_gen();
} else { // ascii
str+=res[ptr++];
++column;
}
}
tok type=get_type(str);
return {{begin_line, begin_column, line, column, filename}, (type!=tok::null)?type:tok::id, str};
}
token lexer::num_gen() {
u32 begin_line=line;
u32 begin_column=column;
// generate hex number
if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='x') {
string str="0x";
ptr+=2;
while(ptr<res.size() && is_hex(res[ptr])) {
str+=res[ptr++];
}
column+=str.length();
if (str.length()<3) { // "0x"
err.err("lexer", {begin_line, begin_column, line, column, filename}, "invalid number `"+str+"`");
}
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
} else if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='o') { // generate oct number
string str="0o";
ptr+=2;
while(ptr<res.size() && is_oct(res[ptr])) {
str+=res[ptr++];
}
bool erfmt=false;
while(ptr<res.size() && (is_dec(res[ptr]) || is_hex(res[ptr]))) {
erfmt=true;
str+=res[ptr++];
}
column+=str.length();
if (str.length()==2 || erfmt) {
err.err("lexer", {begin_line, begin_column, line, column, filename}, "invalid number `"+str+"`");
}
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
}
// generate dec number
// dec number -> [0~9][0~9]*(.[0~9]*)(e|E(+|-)0|[1~9][0~9]*)
string str="";
while(ptr<res.size() && is_dec(res[ptr])) {
str+=res[ptr++];
}
if (ptr<res.size() && res[ptr]=='.') {
str+=res[ptr++];
while(ptr<res.size() && is_dec(res[ptr])) {
str+=res[ptr++];
}
// "xxxx." is not a correct number
if (str.back()=='.') {
column+=str.length();
err.err("lexer",{begin_line, begin_column, line, column, filename}, "invalid number `"+str+"`");
return {{begin_line, begin_column, line, column, filename}, tok::num, "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() && is_dec(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();
err.err("lexer",{begin_line, begin_column, line, column, filename}, "invalid number `"+str+"`");
return {{begin_line, begin_column, line, column, filename}, tok::num, "0"};
}
}
column+=str.length();
return {{begin_line, begin_column, line, column, filename}, tok::num, str};
}
token lexer::str_gen() {
u32 begin_line=line;
u32 begin_column=column;
string str="";
const 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 'e': str+='\033'; 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;
}
if (res[ptr]=='\n') {
column=0;
++line;
}
continue;
}
str+=res[ptr];
}
// check if this string ends with a " or '
if (ptr++>=res.size()) {
err.err("lexer", {begin_line, begin_column, line, column, filename}, "get EOF when generating string");
return {{begin_line, begin_column, line, column, filename}, tok::str, str};
}
++column;
if (begin=='`' && str.length()!=1) {
err.err("lexer", {begin_line, begin_column, line, column, filename}, "\'`\' is used for string including one character");
}
return {{begin_line, begin_column, line, column, filename}, tok::str, str};
}
token lexer::single_opr() {
u32 begin_line=line;
u32 begin_column=column;
string str(1,res[ptr]);
++column;
tok type=get_type(str);
if (type==tok::null) {
err.err("lexer", {begin_line, begin_column, line, column, filename}, "invalid operator `"+str+"`");
}
++ptr;
return {{begin_line, begin_column, line, column, filename}, type, str};
}
token lexer::dots() {
u32 begin_line=line;
u32 begin_column=column;
string str=".";
if (ptr+2<res.size() && res[ptr+1]=='.' && res[ptr+2]=='.') {
str+="..";
}
ptr+=str.length();
column+=str.length();
return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
}
token lexer::calc_opr() {
u32 begin_line=line;
u32 begin_column=column;
// get calculation operator
string str(1,res[ptr++]);
if (ptr<res.size() && res[ptr]=='=') {
str+=res[ptr++];
}
column+=str.length();
return {{begin_line, begin_column, line, column, filename}, get_type(str), str};
}
const error& lexer::scan(const string& file) {
line=1;
column=0;
ptr=0;
open(file);
while(ptr<res.size()) {
while(ptr<res.size() && skip(res[ptr])) {
// these characters will be ignored, and '\n' will cause ++line
++column;
if (res[ptr++]=='\n') {
++line;
column=0;
}
}
if (ptr>=res.size()) {
break;
}
if (is_id(res[ptr])) {
toks.push_back(id_gen());
} else if (is_dec(res[ptr])) {
toks.push_back(num_gen());
} else if (is_str(res[ptr])) {
toks.push_back(str_gen());
} else if (is_single_opr(res[ptr])) {
toks.push_back(single_opr());
} else if (res[ptr]=='.') {
toks.push_back(dots());
} else if (is_calc_opr(res[ptr])) {
toks.push_back(calc_opr());
} else if (res[ptr]=='#') {
skip_note();
} else {
err_char();
}
}
toks.push_back({{line, column, line, column, filename}, tok::eof, "<eof>"});
res="";
return err;
}

View File

@ -1,233 +0,0 @@
#pragma once
#include "nasal.h"
#include "nasal_builtin.h"
#include <iostream>
enum op_code_type:u8 {
op_exit, // stop the virtual machine
op_intg, // global scope size, set stack top
op_intl, // local scope size
op_loadg, // load global value
op_loadl, // load local value
op_loadu, // load upvalue
op_pnum, // push constant number to the stack
op_pnil, // push constant nil to the stack
op_pstr, // push constant string to the stack
op_newv, // push new vector with initial values from stack
op_newh, // push new hash to the stack
op_newf, // push new function to the stack
op_happ, // hash append
op_para, // normal parameter
op_deft, // default parameter
op_dyn, // dynamic parameter
op_lnot, // ! logical negation
op_usub, // - negation
op_bnot, // ~ bitwise not static_cast<i32>
op_btor, // | bitwise or
op_btxor, // ^ bitwise xor
op_btand, // & bitwise and
op_add, // +
op_sub, // -
op_mul, // *
op_div, // /
op_lnk, // ~
op_addc, // + const
op_subc, // - const
op_mulc, // * const
op_divc, // / const
op_lnkc, // ~ const
op_addeq, // += maybe pop stack top
op_subeq, // -= maybe pop stack top
op_muleq, // *= maybe pop stack top
op_diveq, // /= maybe pop stack top
op_lnkeq, // ~= maybe pop stack top
op_btandeq,// &= maybe pop stack top
op_btoreq, // |= maybe pop stack top
op_btxoreq,// ^= maybe pop stack top
op_addeqc, // += const don't pop stack top
op_subeqc, // -= const don't pop stack top
op_muleqc, // *= const don't pop stack top
op_diveqc, // /= const don't pop stack top
op_lnkeqc, // ~= const don't pop stack top
op_addecp, // += const and pop stack top
op_subecp, // -= const and pop stack top
op_mulecp, // *= const and pop stack top
op_divecp, // /= const and pop stack top
op_lnkecp, // ~= concat const string and pop stack top
op_meq, // = maybe pop stack top
op_eq, // ==
op_neq, // !=
op_less, // <
op_leq, // <=
op_grt, // >
op_geq, // >=
op_lessc, // < const
op_leqc, // <= const
op_grtc, // > const
op_geqc, // >= const
op_pop, // pop a value out of stack top
op_jmp, // jump absolute address with no condition
op_jt, // used in operator and/or,jmp when condition is true and DO NOT POP
op_jf, // used in conditional/loop,jmp when condition is false and POP STACK
op_cnt, // add counter for forindex/foreach
op_findex, // index counter on the top of forindex_stack plus 1
op_feach, // index counter on the top of forindex_stack plus 1 and get the value in vector
op_callg, // get value in global scope
op_calll, // get value in local scope
op_upval, // get value in closure, high 16 as the index of upval, low 16 as the index of local
op_callv, // call vec[index]
op_callvi, // call vec[immediate] (used in multi-assign/multi-define)
op_callh, // call hash.label
op_callfv, // call function(vector as parameters)
op_callfh, // call function(hash as parameters)
op_callb, // call builtin-function
op_slcbeg, // begin of slice like: vec[1,2,3:6,0,-1]
op_slcend, // end of slice
op_slc, // slice like vec[1]
op_slc2, // slice like vec[nil:10]
op_mcallg, // get memory space of value in global scope
op_mcalll, // get memory space of value in local scope
op_mupval, // get memory space of value in closure
op_mcallv, // get memory space of vec[index]
op_mcallh, // get memory space of hash.label
op_ret // return
};
const char* opname[]={
"exit ","intg ","intl ","loadg ",
"loadl ","loadu ","pnum ","pnil ",
"pstr ","newv ","newh ","newf ",
"happ ","para ","def ","dyn ",
"lnot ","usub ","bnot ","btor ",
"btxor ","btand ","add ","sub ",
"mult ","div ","lnk ","addc ",
"subc ","multc ","divc ","lnkc ",
"addeq ","subeq ","muleq ","diveq ",
"lnkeq ","bandeq","boreq ","bxoreq",
"addeqc","subeqc","muleqc","diveqc",
"lnkeqc","addecp","subecp","mulecp",
"divecp","lnkecp","meq ","eq ",
"neq ","less ","leq ","grt ",
"geq ","lessc ","leqc ","grtc ",
"geqc ","pop ","jmp ","jt ",
"jf ","cnt ","findx ","feach ",
"callg ","calll ","upval ","callv ",
"callvi","callh ","callfv","callfh",
"callb ","slcbeg","slcend","slc ",
"slc2 ","mcallg","mcalll","mupval",
"mcallv","mcallh","ret "
};
struct opcode {
u8 op; // opcode
u16 fidx; // source code file index
u32 num; // immediate num
u32 line; // location line of source code
opcode() = default;
opcode(const opcode&) = default;
opcode& operator=(const opcode&) = default;
};
class codestream {
private:
opcode code;
const u32 index;
static const f64* nums;
static const string* strs;
static const string* files;
public:
codestream(const opcode& c, const u32 i): code(c), index(i) {}
static void set(const f64*, const string*, const string*);
void dump(std::ostream&) const;
};
const f64* codestream::nums=nullptr;
const string* codestream::strs=nullptr;
const string* codestream::files=nullptr;
void codestream::set(
const f64* numbuff,
const string* strbuff,
const string* filelist=nullptr
) {
nums=numbuff;
strs=strbuff;
files=filelist;
}
void codestream::dump(std::ostream& out) const {
using std::setw;
using std::setfill;
using std::hex;
using std::dec;
auto op=code.op;
auto num=code.num;
out<<hex<<"0x"
<<setw(6)<<setfill('0')<<index<<" "
<<setw(2)<<setfill('0')<<(u32)op<<" "
<<setw(2)<<setfill('0')<<((num>>16)&0xff)<<" "
<<setw(2)<<setfill('0')<<((num>>8)&0xff)<<" "
<<setw(2)<<setfill('0')<<(num&0xff)<<" "
<<opname[op]<<" "<<dec;
switch(op) {
case op_addeq: case op_subeq:
case op_muleq: case op_diveq:
case op_lnkeq: case op_meq:
case op_btandeq: case op_btoreq:
case op_btxoreq:
out<<hex<<"0x"<<num<<dec<<" sp-"<<num;break;
case op_addeqc: case op_subeqc:
case op_muleqc:case op_diveqc:
out<<hex<<"0x"<<num<<dec<<" ("<<nums[num]<<")";break;
case op_lnkeqc:
out<<hex<<"0x"<<num<<dec<<" ("<<rawstr(strs[num], 16)<<")";break;
case op_addecp: case op_subecp:
case op_mulecp: case op_divecp:
out<<hex<<"0x"<<num<<dec<<" ("<<nums[num]<<") sp-1";break;
case op_lnkecp:
out<<hex<<"0x"<<num<<dec<<" ("<<rawstr(strs[num], 16)<<") sp-1";break;
case op_addc: case op_subc:
case op_mulc: case op_divc:
case op_lessc: case op_leqc:
case op_grtc: case op_geqc:
case op_pnum:
out<<hex<<"0x"<<num<<dec<<" ("<<nums[num]<<")";break;
case op_callvi: case op_newv:
case op_callfv: case op_intg:
case op_intl: case op_findex:
case op_feach: case op_newf:
case op_jmp: case op_jt:
case op_jf: case op_callg:
case op_mcallg: case op_loadg:
case op_calll: case op_mcalll:
case op_loadl:
out<<hex<<"0x"<<num<<dec;break;
case op_callb:
out<<hex<<"0x"<<num<<" <"<<builtin[num].name
<<"@0x"<<(u64)builtin[num].func<<dec<<">";break;
case op_upval: case op_mupval:
case op_loadu:
out<<hex<<"0x"<<((num>>16)&0xffff)
<<"[0x"<<(num&0xffff)<<"]"<<dec;break;
case op_happ: case op_pstr:
case op_lnkc: case op_callh:
case op_mcallh: case op_para:
case op_deft: case op_dyn:
out<<hex<<"0x"<<num<<dec<<" ("<<rawstr(strs[num], 16)<<")";break;
default:
if (files) {
out<<hex<<"0x"<<num<<dec;
}
break;
}
if (files) {
out<<"("<<files[code.fidx]<<":"<<code.line<<")";
}
}
std::ostream& operator<<(std::ostream& out, const codestream& ins) {
ins.dump(out);
return out;
}

View File

@ -1,75 +0,0 @@
#pragma once
#include <cmath>
#include "nasal_ast.h"
void const_str(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(ast& root) {
auto& vec=root.child();
f64 res=0;
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;
case ast_bitor: res=i32(vec[0].num())|i32(vec[1].num()); break;
case ast_bitxor: res=i32(vec[0].num())^i32(vec[1].num()); break;
case ast_bitand: res=i32(vec[0].num())&i32(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(ast& root) {
auto& vec=root.child();
for(auto& i:vec) {
calc_const(i);
}
if (vec.size()==1 && root.type()==ast_neg && vec[0].type()==ast_num) {
f64 res=-vec[0].num();
root.set_num(res);
root.child().clear();
root.set_type(ast_num);
return;
}
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 && root.type()!=ast_bitor &&
root.type()!=ast_bitxor && root.type()!=ast_bitand) {
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(ast& root) {
for(auto& i:root.child()) {
calc_const(i);
}
}

File diff suppressed because it is too large Load Diff

1287
nasal_vm.h

File diff suppressed because it is too large Load Diff

View File

@ -148,6 +148,14 @@ bool ast_dumper::visit_ternary_operator(ternary_operator* node) {
}
bool ast_dumper::visit_binary_operator(binary_operator* node) {
if (node->get_optimized_number()) {
node->get_optimized_number()->accept(this);
return true;
}
if (node->get_optimized_string()) {
node->get_optimized_string()->accept(this);
return true;
}
dump_indent();
std::cout << "binary_operator ";
switch(node->get_operator_type()) {
@ -178,6 +186,10 @@ bool ast_dumper::visit_binary_operator(binary_operator* node) {
}
bool ast_dumper::visit_unary_operator(unary_operator* node) {
if (node->get_optimized_number()) {
node->get_optimized_number()->accept(this);
return true;
}
dump_indent();
std::cout << "unary_operator ";
switch(node->get_operator_type()) {

View File

@ -119,6 +119,12 @@ binary_operator::~binary_operator() {
if (right) {
delete right;
}
if (optimized_const_number) {
delete optimized_const_number;
}
if (optimized_const_string) {
delete optimized_const_string;
}
}
void binary_operator::accept(ast_visitor* visitor) {
@ -129,6 +135,9 @@ unary_operator::~unary_operator() {
if (value) {
delete value;
}
if (optimized_number) {
delete optimized_number;
}
}
void unary_operator::accept(ast_visitor* visitor) {

View File

@ -299,18 +299,26 @@ private:
binary_type type;
expr* left;
expr* right;
number_literal* optimized_const_number;
string_literal* optimized_const_string;
public:
binary_operator(const span& location):
expr(location, expr_type::ast_binary),
left(nullptr), right(nullptr) {}
left(nullptr), right(nullptr),
optimized_const_number(nullptr),
optimized_const_string(nullptr) {}
~binary_operator();
void set_operator_type(binary_type operator_type) {type = operator_type;}
void set_left(expr* node) {left = node;}
void set_right(expr* node) {right = node;}
void set_optimized_number(number_literal* node) {optimized_const_number = node;}
void set_optimized_string(string_literal* node) {optimized_const_string = node;}
binary_type get_operator_type() const {return type;}
expr* get_left() {return left;}
expr* get_right() {return right;}
number_literal* get_optimized_number() {return optimized_const_number;}
string_literal* get_optimized_string() {return optimized_const_string;}
void accept(ast_visitor*) override;
};
@ -325,16 +333,19 @@ public:
private:
unary_type type;
expr* value;
number_literal* optimized_number;
public:
unary_operator(const span& location):
expr(location, expr_type::ast_unary),
value(nullptr) {}
value(nullptr), optimized_number(nullptr) {}
~unary_operator();
void set_operator_type(unary_type operator_type) {type = operator_type;}
void set_value(expr* node) {value = node;}
void set_optimized_number(number_literal* node) {optimized_number = node;}
unary_type get_operator_type() const {return type;}
expr* get_value() {return value;}
number_literal* get_optimized_number() {return optimized_number;}
void accept(ast_visitor*) override;
};

View File

@ -829,6 +829,16 @@ void codegen::unary_gen(unary_operator* node) {
}
void codegen::binary_gen(binary_operator* node) {
// generate optimized result
if (node->get_optimized_number()) {
num_gen(node->get_optimized_number());
return;
}
if (node->get_optimized_string()) {
str_gen(node->get_optimized_string());
return;
}
switch(node->get_operator_type()) {
case binary_operator::binary_type::condition_or: or_gen(node); return;
case binary_operator::binary_type::condition_and: and_gen(node); return;

View File

@ -29,7 +29,6 @@
#include <thread>
#include "nasal_new_header.h"
#include "nasal_new_err.h"
enum vm_type:u8 {
/* none-gc object */

View File

@ -49,3 +49,5 @@ f64 str2num(const char*);
i32 utf8_hdchk(const char);
std::string chrhex(const char);
std::string rawstr(const std::string&, const usize maxlen=0);
#include "nasal_new_gc.h"

View File

@ -1,18 +1,129 @@
#include "optimizer.h"
void optimizer::const_string(binary_operator* node) {
void optimizer::const_string(
binary_operator* node,
string_literal* left_node,
string_literal* right_node) {
if (node->get_operator_type()!=binary_operator::binary_type::concat) {
return;
}
const auto& left = left_node->get_content();
const auto& right = right_node->get_content();
node->set_optimized_string(
new string_literal(node->get_location(), left+right));
}
void optimizer::const_number(binary_operator* node) {
void optimizer::const_number(
binary_operator* node,
number_literal* left_node,
number_literal* right_node) {
const auto left = left_node->get_number();
const auto right = right_node->get_number();
f64 res;
switch(node->get_operator_type()) {
case binary_operator::binary_type::add: res = left+right; break;
case binary_operator::binary_type::sub: res = left-right; break;
case binary_operator::binary_type::mult: res = left*right; break;
case binary_operator::binary_type::div: res = left/right; break;
case binary_operator::binary_type::less: res = left<right; break;
case binary_operator::binary_type::leq: res = left<=right; break;
case binary_operator::binary_type::grt: res = left>right; break;
case binary_operator::binary_type::geq: res = left>=right; break;
case binary_operator::binary_type::bitwise_or:
res = i32(left)|i32(right); break;
case binary_operator::binary_type::bitwise_xor:
res = i32(left)^i32(right); break;
case binary_operator::binary_type::bitwise_and:
res = i32(left)&i32(right); break;
default: return;
}
if (std::isinf(res) || std::isnan(res)) {
return;
}
node->set_optimized_number(
new number_literal(node->get_location(), res));
}
void optimizer::const_number(
unary_operator* node,
number_literal* value_node) {
auto res = value_node->get_number();
switch(node->get_operator_type()) {
case unary_operator::unary_type::negative: res = -res; break;
case unary_operator::unary_type::bitwise_not: res = ~i32(res); break;
case unary_operator::unary_type::logical_not: res = !res; break;
}
if (std::isinf(res) || std::isnan(res)) {
return;
}
node->set_optimized_number(
new number_literal(node->get_location(), res));
}
bool optimizer::visit_binary_operator(binary_operator* node) {
node->get_left()->accept(this);
node->get_right()->accept(this);
number_literal* left_num_node = nullptr;
number_literal* right_num_node = nullptr;
string_literal* left_str_node = nullptr;
string_literal* right_str_node = nullptr;
if (node->get_left()->get_type()==expr_type::ast_num) {
left_num_node = (number_literal*)node->get_left();
} else if (node->get_left()->get_type()==expr_type::ast_binary &&
((binary_operator*)node->get_left())->get_optimized_number()) {
left_num_node = ((binary_operator*)node->get_left())->get_optimized_number();
} else if (node->get_left()->get_type()==expr_type::ast_unary &&
((unary_operator*)node->get_left())->get_optimized_number()) {
left_num_node = ((unary_operator*)node->get_left())->get_optimized_number();
}
if (node->get_right()->get_type()==expr_type::ast_num) {
right_num_node = (number_literal*)node->get_right();
} else if (node->get_right()->get_type()==expr_type::ast_binary &&
((binary_operator*)node->get_right())->get_optimized_number()) {
right_num_node = ((binary_operator*)node->get_right())->get_optimized_number();
} else if (node->get_right()->get_type()==expr_type::ast_unary &&
((unary_operator*)node->get_right())->get_optimized_number()) {
right_num_node = ((unary_operator*)node->get_right())->get_optimized_number();
}
if (node->get_left()->get_type()==expr_type::ast_str) {
left_str_node = (string_literal*)node->get_left();
} else if (node->get_left()->get_type()==expr_type::ast_binary &&
((binary_operator*)node->get_left())->get_optimized_string()) {
left_str_node = ((binary_operator*)node->get_left())->get_optimized_string();
}
if (node->get_right()->get_type()==expr_type::ast_str) {
right_str_node = (string_literal*)node->get_right();
} else if (node->get_right()->get_type()==expr_type::ast_binary &&
((binary_operator*)node->get_right())->get_optimized_string()) {
right_str_node = ((binary_operator*)node->get_right())->get_optimized_string();
}
if (left_num_node && right_num_node) {
const_number(node, left_num_node, right_num_node);
return true;
}
if (left_str_node && right_str_node) {
const_string(node, left_str_node, right_str_node);
return true;
}
return true;
}
bool optimizer::visit_unary_operator(unary_operator* node) {
node->get_value()->accept(this);
number_literal* value_node = nullptr;
if (node->get_value()->get_type()==expr_type::ast_num) {
value_node = (number_literal*)node->get_value();
} else if (node->get_value()->get_type()==expr_type::ast_binary &&
((binary_operator*)node->get_value())->get_optimized_number()) {
value_node = ((binary_operator*)node->get_value())->get_optimized_number();
} else if (node->get_value()->get_type()==expr_type::ast_unary &&
((unary_operator*)node->get_value())->get_optimized_number()) {
value_node = ((unary_operator*)node->get_value())->get_optimized_number();
}
if (value_node) {
const_number(node, value_node);
}
return true;
}

View File

@ -7,8 +7,9 @@
class optimizer:public ast_visitor {
private:
void const_string(binary_operator*);
void const_number(binary_operator*);
void const_string(binary_operator*, string_literal*, string_literal*);
void const_number(binary_operator*, number_literal*, number_literal*);
void const_number(unary_operator*, number_literal*);
public:
bool visit_binary_operator(binary_operator*) override;
@ -17,67 +18,3 @@ public:
public:
void do_optimization(code_block*);
};
// void const_str(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(ast& root) {
// auto& vec=root.child();
// f64 res=0;
// 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;
// case ast_bitor: res=i32(vec[0].num())|i32(vec[1].num()); break;
// case ast_bitxor: res=i32(vec[0].num())^i32(vec[1].num()); break;
// case ast_bitand: res=i32(vec[0].num())&i32(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(ast& root) {
// auto& vec=root.child();
// for(auto& i:vec) {
// calc_const(i);
// }
// if (vec.size()==1 && root.type()==ast_neg && vec[0].type()==ast_num) {
// f64 res=-vec[0].num();
// root.set_num(res);
// root.child().clear();
// root.set_type(ast_num);
// return;
// }
// 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 && root.type()!=ast_bitor &&
// root.type()!=ast_bitxor && root.type()!=ast_bitand) {
// 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);
// }
// }

View File

@ -2,7 +2,7 @@ import.std.padding;
import.std.file;
import.std.sort;
var source=find_all_files_with_extension(".","cpp","h");
var source=find_all_files_with_extension("./src","cpp","h");
sort(source,func(a,b){return cmp(a,b)<0});
var lib=find_all_files_with_extension("./std","nas");
@ -78,7 +78,7 @@ var calc=func(codetype,files,path=""){
return int(bytes/1024);
}
var all=calc("source code:",source)
var all=calc("source code:",source,"src/")
+calc("lib:",lib,"std/")
+calc("test file:",testfile,"test/")
+calc("module:",module,"module/");

View File

@ -54,9 +54,12 @@ var filechecksum=func(){
foreach(var p;find_all_files_with_extension("./module","nas","cpp")) {
append(files,"./module/"~p);
}
foreach(var p;find_all_files_with_extension(".","cpp","h","md")) {
foreach(var p;find_all_files_with_extension(".","md")) {
append(files,"./"~p);
}
foreach(var p;find_all_files_with_extension("./src","cpp","h")) {
append(files,"./src/"~p);
}
foreach(var p;find_all_files_with_extension("./doc","md")) {
append(files,"./doc/"~p);
}