✨ change build scripts
This commit is contained in:
parent
ba6b7cd05c
commit
ac2744e24f
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -61,4 +49,28 @@ if(NOT CMAKE_HOST_SYSTEM_NAME MATCHES "Windows")
|
|||
${CMAKE_SOURCE_DIR}/build/nasal
|
||||
${CMAKE_SOURCE_DIR}/nasal
|
||||
)
|
||||
endif()
|
||||
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
178
main.cpp
|
@ -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;
|
||||
}
|
32
makefile
32
makefile
|
@ -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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// module for test
|
||||
|
||||
#include <iostream>
|
||||
#include "../nasal.h"
|
||||
#include "../src/nasal_new_header.h"
|
||||
|
||||
namespace nasal_fib_module {
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "../nasal.h"
|
||||
#include "../src/nasal_new_header.h"
|
||||
#include <iostream>
|
||||
|
||||
#ifndef _MSC_VER
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "../nasal.h"
|
||||
#include "../src/nasal_new_header.h"
|
||||
#include <cmath>
|
||||
|
||||
var nas_vec2(var* args, usize size, gc* ngc) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#include "../nasal.h"
|
||||
#include "../src/nasal_new_header.h"
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <unistd.h>
|
||||
|
|
255
nasal.h
255
nasal.h
|
@ -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
|
301
nasal_ast.h
301
nasal_ast.h
|
@ -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;
|
||||
}
|
1400
nasal_builtin.h
1400
nasal_builtin.h
File diff suppressed because it is too large
Load Diff
1088
nasal_codegen.h
1088
nasal_codegen.h
File diff suppressed because it is too large
Load Diff
321
nasal_dbg.h
321
nasal_dbg.h
|
@ -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;
|
||||
}
|
228
nasal_err.h
228
nasal_err.h
|
@ -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";
|
||||
}
|
928
nasal_gc.h
928
nasal_gc.h
|
@ -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*);
|
226
nasal_import.h
226
nasal_import.h
|
@ -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;
|
||||
}
|
506
nasal_lexer.h
506
nasal_lexer.h
|
@ -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;
|
||||
}
|
233
nasal_opcode.h
233
nasal_opcode.h
|
@ -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;
|
||||
}
|
75
nasal_opt.h
75
nasal_opt.h
|
@ -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);
|
||||
}
|
||||
}
|
1111
nasal_parse.h
1111
nasal_parse.h
File diff suppressed because it is too large
Load Diff
1287
nasal_vm.h
1287
nasal_vm.h
File diff suppressed because it is too large
Load Diff
|
@ -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()) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include <thread>
|
||||
|
||||
#include "nasal_new_header.h"
|
||||
#include "nasal_new_err.h"
|
||||
|
||||
enum vm_type:u8 {
|
||||
/* none-gc object */
|
||||
|
|
|
@ -48,4 +48,6 @@ f64 dec2f(const char*);
|
|||
f64 str2num(const char*);
|
||||
i32 utf8_hdchk(const char);
|
||||
std::string chrhex(const char);
|
||||
std::string rawstr(const std::string&, const usize maxlen=0);
|
||||
std::string rawstr(const std::string&, const usize maxlen=0);
|
||||
|
||||
#include "nasal_new_gc.h"
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
// }
|
||||
// }
|
|
@ -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/");
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue