Merge pull request #41 from ValKmjolnir/develop

📝 adjust CI & improve maintainability
This commit is contained in:
ValK 2024-05-16 00:17:18 +08:00 committed by GitHub
commit 4f0afb31db
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 821 additions and 623 deletions

View File

@ -1,4 +1,4 @@
name: C/C++ CI name: Build/Test/Package CI
on: on:
schedule: schedule:
@ -10,62 +10,48 @@ on:
workflow_dispatch: workflow_dispatch:
jobs: jobs:
mac-build: mac-aarch64-build:
runs-on: macos-latest runs-on: macos-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: make - name: Build
run: | run: |
make -j4 make -j4
cd module cd module
make all -j4 make all -j4
cd .. cd ..
make test - name: Test
tar -czf nasal-mac-nightly.tgz . run: make test
python3 tools/pack.py - name: Package
- name: Release file run: python3 tools/pack.py
# You may pin to the exact commit or the version. - name: Release
# uses: djnicholson/release-action@e9a535b3eced09c460e07a84118fb74ae9b53236 uses: softprops/action-gh-release@v2.0.5
uses: marvinpinto/action-automatic-releases@v1.2.1
with: with:
# GitHub auth token name: macOS nightly build
repo_token: ${{ secrets.GITHUB_TOKEN }} tag_name: next_macOS
# Name of Release to add file to prerelease: true
title: macOS Nightly build
# Name of the tag for the release (will be associated with current branch)
automatic_release_tag: next_macOS
# File to release
files: | files: |
nasal-mac-nightly.tgz
nasal-Darwin.tar nasal-Darwin.tar
linux-x86_64-build: linux-x86_64-build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: make - name: Build
run: | run: |
make -j4 make -j4
cd module cd module
make all -j4 make all -j4
cd .. cd ..
make test - name: Test
touch nasal-linux-x86_64-nightly.tgz run: make test
tar -czf nasal-linux-x86_64-nightly.tgz --exclude=nasal-linux-x86_64-nightly.tgz . - name: Package
python3 tools/pack.py run: python3 tools/pack.py
- name: Release file - name: Release
# You may pin to the exact commit or the version. uses: softprops/action-gh-release@v2.0.5
# uses: djnicholson/release-action@e9a535b3eced09c460e07a84118fb74ae9b53236
uses: marvinpinto/action-automatic-releases@v1.2.1
with: with:
# GitHub auth token name: linux nightly build
repo_token: ${{ secrets.GITHUB_TOKEN }} tag_name: next_linux_x86_64
# Name of Release to add file to prerelease: true
title: Linux Nightly build
# Name of the tag for the release (will be associated with current branch)
automatic_release_tag: next_linux_x86_64
# File to release
files: | files: |
nasal-linux-x86_64-nightly.tgz
nasal-Linux.tar nasal-Linux.tar

View File

@ -2,7 +2,6 @@
<img src="./doc/pic/header.png" style="width:600px"></img> <img src="./doc/pic/header.png" style="width:600px"></img>
![GitHub code size](https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![GitHub release(latest by date)](https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github) ![GitHub release(latest by date)](https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
[![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](./LICENSE) [![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](./LICENSE)
![downloads](https://img.shields.io/github/downloads/ValKmjolnir/Nasal-Interpreter/total.svg?style=flat-square&logo=github) ![downloads](https://img.shields.io/github/downloads/ValKmjolnir/Nasal-Interpreter/total.svg?style=flat-square&logo=github)
@ -13,7 +12,8 @@
## __Contents__ ## __Contents__
* [__Introduction__](#introduction) * [__Introduction__](#introduction)
* [__Compile__](#how-to-compile) * [__Download__](#download)
* [__Compile__](#compile)
* [__Usage__](#how-to-use) * [__Usage__](#how-to-use)
* [__Tutorial__](./doc/tutorial.md) * [__Tutorial__](./doc/tutorial.md)
* [__Release Notes__](./doc/dev.md#release-notes) * [__Release Notes__](./doc/dev.md#release-notes)
@ -42,11 +42,10 @@ __Contact us if having great ideas to share!__
[Nasal](http://wiki.flightgear.org/Nasal_scripting_language) [Nasal](http://wiki.flightgear.org/Nasal_scripting_language)
is an ECMAscript-like language used in [FlightGear](https://www.flightgear.org/). is an ECMAscript-like language used in [FlightGear](https://www.flightgear.org/).
The designer is [Andy Ross](https://github.com/andyross). The designer is [Andy Ross](https://github.com/andyross).
This interpreter is rewritten by [ValKmjolnir](https://github.com/ValKmjolnir) using `C++`(`-std=c++17`). This interpreter is rewritten by [ValKmjolnir](https://github.com/ValKmjolnir) using `C++`(`-std=c++17`).
We really appreciate that Andy created this amazing programming language: [Andy Ross's nasal interpreter](https://github.com/andyross/nasal). We really appreciate that Andy created this amazing programming language: [Andy Ross's nasal interpreter](https://github.com/andyross/nasal).
This project uses __MIT license__ (2019/7 ~ 2021/5/4 ~ 2023/5), __GPL v2 license__ (since 2023/6). Old version of this project uses __MIT license__ (2019/7 ~ 2021/5/4 ~ 2023/5). Now it uses __GPL v2 license__ (since 2023/6).
### __Why writing this Nasal interpreter?__ ### __Why writing this Nasal interpreter?__
@ -64,7 +63,17 @@ interesting programs and run them without the lib of Flightgear.
You could add your own modules to make You could add your own modules to make
the interpreter a useful tool in your own projects. the interpreter a useful tool in your own projects.
## __How to Compile__ ## __Download__
Nightly build could be found here.
Windows nightly build is not supported yet,
please wait or just compile it by yourself, a Cmake file is given for Visual Studio to compile this project easily:
* [macOS-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_macOS)
* [linux-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_linux_x86_64)
* windows-nightly-build: [WIP]
## __Compile__
![g++](https://img.shields.io/badge/GNU-g++-A42E2B?style=flat-square&logo=GNU) ![g++](https://img.shields.io/badge/GNU-g++-A42E2B?style=flat-square&logo=GNU)
![clang++](https://img.shields.io/badge/LLVM-clang++-262D3A?style=flat-square&logo=LLVM) ![clang++](https://img.shields.io/badge/LLVM-clang++-262D3A?style=flat-square&logo=LLVM)

View File

@ -2,7 +2,6 @@
<img src="../doc/pic/header.png" style="width:600px"></img> <img src="../doc/pic/header.png" style="width:600px"></img>
![GitHub code size](https://img.shields.io/github/languages/code-size/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
![GitHub release(latest by date)](https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github) ![GitHub release(latest by date)](https://img.shields.io/github/v/release/ValKmjolnir/Nasal-Interpreter?style=flat-square&logo=github)
[![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](../LICENSE) [![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](../LICENSE)
![downloads](https://img.shields.io/github/downloads/ValKmjolnir/Nasal-Interpreter/total.svg?style=flat-square&logo=github) ![downloads](https://img.shields.io/github/downloads/ValKmjolnir/Nasal-Interpreter/total.svg?style=flat-square&logo=github)
@ -13,6 +12,7 @@
## __目录__ ## __目录__
* [__简介__](#简介) * [__简介__](#简介)
* [__下载__](#下载)
* [__编译__](#编译) * [__编译__](#编译)
* [__使用方法__](#使用方法) * [__使用方法__](#使用方法)
* [__教程__](../doc/tutorial_zh.md) * [__教程__](../doc/tutorial_zh.md)
@ -42,18 +42,28 @@ __如果有好的意见或建议欢迎联系我们!__
[Nasal](http://wiki.flightgear.org/Nasal_scripting_language) [Nasal](http://wiki.flightgear.org/Nasal_scripting_language)
是一款语法与 ECMAscript 相似的编程语言,并作为脚本语言被著名开源飞行模拟器 [FlightGear](https://www.flightgear.org/) 所使用。 是一款语法与 ECMAscript 相似的编程语言,并作为脚本语言被著名开源飞行模拟器 [FlightGear](https://www.flightgear.org/) 所使用。
该语言的设计者为 [Andy Ross](https://github.com/andyross)。 该语言的设计者为 [Andy Ross](https://github.com/andyross)。
该解释器由 [ValKmjolnir](https://github.com/ValKmjolnir) 使用 `C++`(`-std=c++17`)重新实现。非常感谢 Andy 为我们设计了这个神奇且简洁的编程语言: [Andy Ross 的 nasal 解释器](https://github.com/andyross/nasal)。
解释器项目由 [ValKmjolnir](https://github.com/ValKmjolnir) 使用 `C++`(`-std=c++17`)重新实现我们非常感谢Andy为我们带来了这样一个神奇且简洁的编程语言: [Andy Ross 的 nasal 解释器](https://github.com/andyross/nasal) 项目旧版本使用 __MIT__ 协议开源 (2019/7 ~ 2021/5/4 ~ 2023/5),从 2023/6 开始新版本使用 __GPL v2__ 协议
该项目使用 __MIT__ 协议开源 (2019/7 ~ 2021/5/4 ~ 2023/5),从 2023/6 开始使用 __GPL v2__ 协议。 ### __为什么重新写 Nasal 解释器?__
### __我们为什么想要重新写一个 Nasal 解释器?__ 2019 年暑假,[FGPRC](https://www.fgprc.org.cn/) 的成员告诉我,在 Flightgear 中提供的 nasal 控制台窗口中进行调试很不方便,仅仅是想检查语法错误,也得花时间打开软件等待加载进去后进行调试。所以我就写了一个全新的解释器来帮助他们检查语法错误以及运行时错误。
2019年暑假[FGPRC](https://www.fgprc.org.cn/) 的成员告诉我在Flightgear中提供的nasal控制台窗口中进行调试很不方便仅仅是想检查语法错误也得花时间打开软件等待加载进去后进行调试。所以我就写了一个全新的解释器来帮助他们检查语法错误以及运行时错误 我编写了 nasal 的词法分析器和语法分析器,以及一个全新的字节码虚拟机,并用这个运行时来进行 nasal 程序的调试。我们发现使用这个解释器来检测语法和运行时错误极大的提高了效率
我编写了nasal的词法分析器和语法分析器以及一个全新的字节码虚拟机并用这个运行时来进行nasal程序的调试。我们发现使用这个解释器来检测语法和运行时错误极大的提高了效率 你也可以使用这个语言来写一些与 Flightgear 运行环境无关的有趣的程序,并用这个解释器来执行。你也可以让解释器来调用你自己编写的模块,使它成为项目中一个非常有用的工具
你也可以使用这个语言来写一些与Flightgear运行环境无关的有趣的程序并用这个解释器来执行。你也可以让解释器来调用你自己编写的模块使它成为项目中一个非常有用的工具。 ## __下载__
现在支持下载预览版(Nightly Build)。
Windows 平台的预览版解释器现在还没配置相关流水线,
请耐心等候或者直接在本地编译。
我们提供了一份 Cmake 文件,可以很方便地在 Visual Studio 中编译:
* [macOS-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_macOS)
* [linux-nightly-build](https://github.com/ValKmjolnir/Nasal-Interpreter/releases/tag/next_linux_x86_64)
* windows-nightly-build: [施工中...]
## __编译__ ## __编译__

View File

@ -255,6 +255,7 @@ clean:
.PHONY: test .PHONY: test
test:nasal test:nasal
@ ./nasal -t -d test/andy_gc_test.nas
@ ./nasal test/argparse_test.nas @ ./nasal test/argparse_test.nas
@ ./nasal -e test/ascii-art.nas @ ./nasal -e test/ascii-art.nas
@ ./nasal -t -d test/bfs.nas @ ./nasal -t -d test/bfs.nas

View File

@ -25,7 +25,7 @@ var fib(var* args, usize size, gc* ngc) {
var quick_fib(var* args, usize size, gc* ngc) { var quick_fib(var* args, usize size, gc* ngc) {
if (!size) { if (!size) {
return nas_err("quick_fib","lack arguments"); return nas_err("quick_fib", "lack arguments");
} }
double num = args[0].to_num(); double num = args[0].to_num();
if (num<2) { if (num<2) {
@ -53,7 +53,9 @@ struct ghost_obj {
void ghost_for_test_destructor(void* ptr) { void ghost_for_test_destructor(void* ptr) {
std::cout << "ghost_for_test::destructor (0x"; std::cout << "ghost_for_test::destructor (0x";
std::cout << std::hex << reinterpret_cast<u64>(ptr) << std::dec << ") {\n"; std::cout << std::hex << reinterpret_cast<u64>(ptr) << std::dec << ") {\n";
delete static_cast<ghost_obj*>(ptr); delete static_cast<ghost_obj*>(ptr);
std::cout << " delete 0x" << std::hex; std::cout << " delete 0x" << std::hex;
std::cout << reinterpret_cast<u64>(ptr) << std::dec << ";\n"; std::cout << reinterpret_cast<u64>(ptr) << std::dec << ";\n";
std::cout << "}\n"; std::cout << "}\n";
@ -62,7 +64,9 @@ void ghost_for_test_destructor(void* ptr) {
void ghost_for_test_gc_marker(void* ptr, std::vector<var>* bfs_queue) { void ghost_for_test_gc_marker(void* ptr, std::vector<var>* bfs_queue) {
std::cout << "ghost_for_test::mark (0x"; std::cout << "ghost_for_test::mark (0x";
std::cout << std::hex << reinterpret_cast<u64>(ptr) << std::dec << ") {\n"; std::cout << std::hex << reinterpret_cast<u64>(ptr) << std::dec << ") {\n";
bfs_queue->push_back(static_cast<ghost_obj*>(ptr)->test_string); bfs_queue->push_back(static_cast<ghost_obj*>(ptr)->test_string);
std::cout << " mark 0x" << std::hex; std::cout << " mark 0x" << std::hex;
std::cout << reinterpret_cast<u64>(ptr) << std::dec << "->test_string;\n"; std::cout << reinterpret_cast<u64>(ptr) << std::dec << "->test_string;\n";
std::cout << "}\n"; std::cout << "}\n";
@ -86,8 +90,10 @@ var set_new_ghost(var* args, usize size, gc* ngc) {
return nil; return nil;
} }
f64 num = args[1].num(); f64 num = args[1].num();
reinterpret_cast<ghost_obj*>(res.ghost().pointer)->number = static_cast<u32>(num); reinterpret_cast<ghost_obj*>(res.ghost().pointer)->number = static_cast<u32>(num);
std::cout << "set_new_ghost: successfully set ghost.number = " << num << "\n"; std::cout << "set_new_ghost: successfully set ghost.number = " << num << "\n";
reinterpret_cast<ghost_obj*>(res.ghost().pointer)->test_string = ngc->newstr("just for test"); reinterpret_cast<ghost_obj*>(res.ghost().pointer)->test_string = ngc->newstr("just for test");
std::cout << "set_new_ghost: successfully set ghost.test_string = just for test\n"; std::cout << "set_new_ghost: successfully set ghost.test_string = just for test\n";
return nil; return nil;

View File

@ -9,9 +9,11 @@
namespace nasal { namespace nasal {
class ast_dumper:public ast_visitor { class ast_dumper: public ast_visitor {
private: private:
std::vector<std::string> indent; std::vector<std::string> indent;
private:
void push_indent() { void push_indent() {
if (indent.size()) { if (indent.size()) {
if (indent.back()=="|--") { if (indent.back()=="|--") {
@ -35,8 +37,7 @@ private:
std::string format_location(const span& location) { std::string format_location(const span& location) {
std::stringstream ss; std::stringstream ss;
ss << " -> "; ss << " -> ";
ss << location.file << ":"; location.dump_begin(ss);
ss << location.begin_line << ":" << location.begin_column + 1;
ss << "\n"; ss << "\n";
return ss.str(); return ss.str();
} }

View File

@ -95,7 +95,8 @@ std::ostream& logo(std::ostream& out) {
} }
std::ostream& version(std::ostream& out) { std::ostream& version(std::ostream& out) {
std::srand(std::time(nullptr)); std::srand(static_cast<u32>(std::time(nullptr)));
f64 num = 0; f64 num = 0;
for(u32 i = 0; i<5; ++i) { for(u32 i = 0; i<5; ++i) {
num = (num+rand())*(1.0/(RAND_MAX+1.0)); num = (num+rand())*(1.0/(RAND_MAX+1.0));
@ -103,6 +104,7 @@ std::ostream& version(std::ostream& out) {
if (num<0.01) { if (num<0.01) {
nasal::parse::easter_egg(); nasal::parse::easter_egg();
} }
out << "nasal interpreter version " << __nasver__; out << "nasal interpreter version " << __nasver__;
out << " " << nasal::get_platform() << " " << nasal::get_arch(); out << " " << nasal::get_platform() << " " << nasal::get_arch();
out << " (" << __DATE__ << " " << __TIME__ << ")\n"; out << " (" << __DATE__ << " " << __TIME__ << ")\n";
@ -150,7 +152,7 @@ void execute(const std::string& file,
} }
// optimizer does simple optimization on ast // optimizer does simple optimization on ast
auto opt = std::unique_ptr<nasal::optimizer>(new nasal::optimizer); auto opt = std::make_unique<nasal::optimizer>();
opt->do_optimization(parse.tree()); opt->do_optimization(parse.tree());
if (cmd&VM_AST) { if (cmd&VM_AST) {
nasal::ast_dumper().dump(parse.tree()); nasal::ast_dumper().dump(parse.tree());
@ -166,22 +168,22 @@ void execute(const std::string& file,
} }
// run // run
auto start = clk::now(); const auto start = clk::now();
if (cmd&VM_DEBUG) { if (cmd&VM_DEBUG) {
auto debugger = std::unique_ptr<nasal::dbg>(new nasal::dbg); auto debugger = std::make_unique<nasal::dbg>();
debugger->run(gen, ld, argv, cmd&VM_PROFILE, cmd&VM_PROF_ALL); debugger->run(gen, ld, argv, cmd&VM_PROFILE, cmd&VM_PROF_ALL);
} else if (cmd&VM_TIME || cmd&VM_EXEC) { } else if (cmd&VM_TIME || cmd&VM_EXEC) {
auto runtime = std::unique_ptr<nasal::vm>(new nasal::vm); auto runtime = std::make_unique<nasal::vm>();
runtime->set_detail_report_info(cmd&VM_DETAIL); runtime->set_detail_report_info(cmd&VM_DETAIL);
runtime->set_limit_mode_flag(cmd&VM_LIMIT); runtime->set_limit_mode_flag(cmd&VM_LIMIT);
runtime->run(gen, ld, argv); runtime->run(gen, ld, argv);
} }
// get running time // get running time
auto end = clk::now(); const auto end = clk::now();
if (cmd&VM_TIME) { if (cmd&VM_TIME) {
std::clog << "process exited after "; std::clog << "process exited after ";
std::clog << (end-start).count()*1.0/den << "s.\n\n"; std::clog << static_cast<f64>((end-start).count())/den << "s.\n\n";
} }
} }
@ -200,7 +202,7 @@ i32 main(i32 argc, const char* argv[]) {
} else if (s=="-v" || s=="--version") { } else if (s=="-v" || s=="--version") {
std::clog << version; std::clog << version;
} else if (s=="-r" || s=="--repl") { } else if (s=="-r" || s=="--repl") {
auto repl = std::unique_ptr<nasal::repl::repl>(new nasal::repl::repl); auto repl = std::make_unique<nasal::repl::repl>();
repl->execute(); repl->execute();
} else if (s[0]!='-') { } else if (s[0]!='-') {
execute(s, {}, VM_EXEC); execute(s, {}, VM_EXEC);
@ -211,7 +213,7 @@ i32 main(i32 argc, const char* argv[]) {
} }
// execute with arguments // execute with arguments
const std::unordered_map<std::string, u32> cmdlst = { const std::unordered_map<std::string, u32> command_list = {
{"--raw-ast", VM_RAW_AST}, {"--raw-ast", VM_RAW_AST},
{"--ast", VM_AST}, {"--ast", VM_AST},
{"-a", VM_AST}, {"-a", VM_AST},
@ -233,12 +235,13 @@ i32 main(i32 argc, const char* argv[]) {
{"--ref-file", VM_REF_FILE}, {"--ref-file", VM_REF_FILE},
{"--limit", VM_LIMIT|VM_EXEC} {"--limit", VM_LIMIT|VM_EXEC}
}; };
u32 cmd = 0;
u32 commands = 0;
std::string filename = ""; std::string filename = "";
std::vector<std::string> vm_argv; std::vector<std::string> vm_argv;
for(i32 i = 1; i<argc; ++i) { for(i32 i = 1; i<argc; ++i) {
if (cmdlst.count(argv[i])) { if (command_list.count(argv[i])) {
cmd |= cmdlst.at(argv[i]); commands |= command_list.at(argv[i]);
} else if (!filename.length()) { } else if (!filename.length()) {
filename = argv[i]; filename = argv[i];
} else { } else {
@ -248,6 +251,7 @@ i32 main(i32 argc, const char* argv[]) {
if (!filename.length()) { if (!filename.length()) {
err(); err();
} }
execute(filename, vm_argv, cmd? cmd:VM_EXEC);
execute(filename, vm_argv, commands? commands:VM_EXEC);
return 0; return 0;
} }

View File

@ -1,7 +1,7 @@
#pragma once #pragma once
#ifndef __nasver__ #ifndef __nasver__
#define __nasver__ "11.1" #define __nasver__ "11.2"
#endif #endif
#include <cstdint> #include <cstdint>
@ -38,7 +38,7 @@ const char* get_platform();
const char* get_arch(); const char* get_arch();
// virtual machine stack depth, both global depth and value stack depth // virtual machine stack depth, both global depth and value stack depth
const u32 STACK_DEPTH = 4096; const u32 VM_STACK_DEPTH = UINT16_MAX;
f64 hex_to_f64(const char*); f64 hex_to_f64(const char*);
f64 oct_to_f64(const char*); f64 oct_to_f64(const char*);

View File

@ -8,7 +8,7 @@
namespace nasal { namespace nasal {
enum class expr_type: u32 { enum class expr_type {
ast_null = 0, // null node ast_null = 0, // null node
ast_use, // use statement ast_use, // use statement
ast_block, // code block ast_block, // code block
@ -65,13 +65,13 @@ public:
expr(const span& location, expr_type node_type): expr(const span& location, expr_type node_type):
nd_loc(location), nd_type(node_type) {} nd_loc(location), nd_type(node_type) {}
virtual ~expr() = default; virtual ~expr() = default;
void set_begin(u32 line, u32 column) { void set_begin(u64 line, u64 column) {
nd_loc.begin_line = line; nd_loc.begin_line = line;
nd_loc.begin_column = column; nd_loc.begin_column = column;
} }
const span& get_location() const {return nd_loc;} const auto& get_location() const { return nd_loc; }
const u32 get_line() const {return nd_loc.begin_line;} const auto get_line() const { return nd_loc.begin_line; }
expr_type get_type() const {return nd_type;} auto get_type() const { return nd_type; }
void update_location(const span& location) { void update_location(const span& location) {
nd_loc.end_line = location.end_line; nd_loc.end_line = location.end_line;
nd_loc.end_column = location.end_column; nd_loc.end_column = location.end_column;

View File

@ -70,7 +70,8 @@ void codegen::regist_number(const f64 num) {
if (const_number_map.count(num)) { if (const_number_map.count(num)) {
return; return;
} }
u32 size = const_number_map.size();
auto size = const_number_map.size();
const_number_map[num] = size; const_number_map[num] = size;
const_number_table.push_back(num); const_number_table.push_back(num);
} }
@ -79,13 +80,14 @@ void codegen::regist_string(const std::string& str) {
if (const_string_map.count(str)) { if (const_string_map.count(str)) {
return; return;
} }
u32 size = const_string_map.size();
auto size = const_string_map.size();
const_string_map[str] = size; const_string_map[str] = size;
const_string_table.push_back(str); const_string_table.push_back(str);
} }
void codegen::find_symbol(code_block* node) { void codegen::find_symbol(code_block* node) {
auto finder = std::unique_ptr<symbol_finder>(new symbol_finder); auto finder = std::make_unique<symbol_finder>();
for(const auto& i : finder->do_find(node)) { for(const auto& i : finder->do_find(node)) {
// check if symbol conflicts with native function name // check if symbol conflicts with native function name
if (native_function_mapper.count(i.name)) { if (native_function_mapper.count(i.name)) {
@ -93,11 +95,11 @@ void codegen::find_symbol(code_block* node) {
continue; continue;
} }
// create new namespace with checking existence of location file // create new namespace with checking existence of location file
if (!experimental_namespace.count(i.location.file)) { if (!nasal_namespace.count(i.location.file)) {
experimental_namespace[i.location.file] = {}; nasal_namespace[i.location.file] = {};
} }
// if in global scope, load global symbol into this namespace // if in global scope, load global symbol into this namespace
auto& scope = experimental_namespace.at(i.location.file); auto& scope = nasal_namespace.at(i.location.file);
if (local.empty() && !scope.count(i.name)) { if (local.empty() && !scope.count(i.name)) {
scope.insert(i.name); scope.insert(i.name);
} }
@ -107,41 +109,47 @@ void codegen::find_symbol(code_block* node) {
} }
void codegen::regist_symbol(const std::string& name) { void codegen::regist_symbol(const std::string& name) {
// regist global if local scope list is empty
if (local.empty()) { if (local.empty()) {
if (global.count(name)) { if (global.count(name)) {
return; return;
} }
i32 index = global.size(); auto index = global.size();
global[name] = index; global[name] = index;
return; return;
} }
if (local.back().count(name)) { if (local.back().count(name)) {
return; return;
} }
i32 index = local.back().size(); auto index = local.back().size();
local.back()[name] = index; local.back()[name] = index;
} }
i32 codegen::local_symbol_find(const std::string& name) { i64 codegen::local_symbol_find(const std::string& name) {
if (local.empty()) { if (local.empty()) {
return -1; return -1;
} }
return local.back().count(name)? local.back().at(name):-1; return local.back().count(name)? local.back().at(name):-1;
} }
i32 codegen::global_symbol_find(const std::string& name) { i64 codegen::global_symbol_find(const std::string& name) {
return global.count(name)? global.at(name):-1; return global.count(name)? global.at(name):-1;
} }
i32 codegen::upvalue_symbol_find(const std::string& name) { i64 codegen::upvalue_symbol_find(const std::string& name) {
// 32768 level 65536 upvalues // 32768 level 65536 upvalues
i32 index = -1; // may cause some errors if local scope depth is too deep or
// local scope's symbol list size is greater than 65536,
// but we check the local size in codegen::func_gen
i64 index = -1;
usize size = local.size(); usize size = local.size();
if (size<=1) { if (size<=1) {
return -1; return -1;
} }
auto iter = local.begin(); auto iter = local.begin();
for(u32 i = 0; i<size-1; ++i, ++iter) { for(u64 i = 0; i<size-1; ++i, ++iter) {
if (iter->count(name)) { if (iter->count(name)) {
index = ((i<<16)|(*iter).at(name)); index = ((i<<16)|(*iter).at(name));
} }
@ -149,7 +157,7 @@ i32 codegen::upvalue_symbol_find(const std::string& name) {
return index; return index;
} }
void codegen::emit(u8 operation_code, u32 immediate_num, const span& location) { void codegen::emit(u8 operation_code, u64 immediate_num, const span& location) {
code.push_back({ code.push_back({
operation_code, operation_code,
static_cast<u16>(file_map.at(location.file)), static_cast<u16>(file_map.at(location.file)),
@ -297,10 +305,14 @@ void codegen::func_gen(function* node) {
block_gen(block); block_gen(block);
in_foreach_loop_level.pop_back(); in_foreach_loop_level.pop_back();
// we must check the local scope symbol list size
// the local scope should not cause stack overflow
// and should not greater than upvalue's max size(65536)
code[lsize].num = local.back().size(); code[lsize].num = local.back().size();
if (local.back().size()>=STACK_DEPTH) { if (local.back().size()>=VM_STACK_DEPTH || local.back().size()>=UINT16_MAX) {
die("too many local variants: " + die("too many local variants: " +
std::to_string(local.back().size()), block->get_location() std::to_string(local.back().size()),
block->get_location()
); );
} }
local.pop_back(); local.pop_back();
@ -346,7 +358,7 @@ void codegen::call_identifier(identifier* node) {
return; return;
} }
i32 index; i64 index;
if ((index = local_symbol_find(name))>=0) { if ((index = local_symbol_find(name))>=0) {
emit(op_calll, index, node->get_location()); emit(op_calll, index, node->get_location());
return; return;
@ -466,7 +478,7 @@ void codegen::mcall_identifier(identifier* node) {
return; return;
} }
i32 index; i64 index;
if ((index = local_symbol_find(name))>=0) { if ((index = local_symbol_find(name))>=0) {
emit(op_mcalll, index, node->get_location()); emit(op_mcalll, index, node->get_location());
return; return;
@ -505,7 +517,7 @@ void codegen::single_def(definition_expr* node) {
const auto& str = node->get_variable_name()->get_name(); const auto& str = node->get_variable_name()->get_name();
calc_gen(node->get_value()); calc_gen(node->get_value());
// only generate in repl mode and in global scope // only generate in repl mode and in global scope
if (need_repl_output && local.empty()) { if (flag_need_repl_output && local.empty()) {
emit(op_repl, 0, node->get_location()); emit(op_repl, 0, node->get_location());
} }
if (local.empty()) { if (local.empty()) {
@ -518,7 +530,7 @@ void codegen::single_def(definition_expr* node) {
void codegen::multi_def(definition_expr* node) { void codegen::multi_def(definition_expr* node) {
auto& identifiers = node->get_variables()->get_variables(); auto& identifiers = node->get_variables()->get_variables();
usize size = identifiers.size(); usize size = identifiers.size();
// (var a,b,c) = (c,b,a); // (var a, b, c) = (c, b, a);
if (node->get_tuple()) { if (node->get_tuple()) {
auto& vals = node->get_tuple()->get_elements(); auto& vals = node->get_tuple()->get_elements();
if (identifiers.size()>vals.size()) { if (identifiers.size()>vals.size()) {
@ -545,7 +557,7 @@ void codegen::multi_def(definition_expr* node) {
} }
return; return;
} }
// (var a,b,c) = [0,1,2]; // (var a, b, c) = [0, 1, 2];
calc_gen(node->get_value()); calc_gen(node->get_value());
for(usize i = 0; i<size; ++i) { for(usize i = 0; i<size; ++i) {
emit(op_callvi, i, node->get_value()->get_location()); emit(op_callvi, i, node->get_value()->get_location());
@ -752,16 +764,16 @@ void codegen::multi_assign_gen(multi_assign* node) {
} }
} }
i32 size = tuple_node->get_elements().size(); i64 size = static_cast<i64>(tuple_node->get_elements().size());
// generate multiple assignment: (a, b, c) = (1, 2, 3); // generate multiple assignment: (a, b, c) = (1, 2, 3);
if (value_node->get_type()==expr_type::ast_tuple) { if (value_node->get_type()==expr_type::ast_tuple) {
const auto& value_tuple = reinterpret_cast<tuple_expr*>(value_node) const auto& value_tuple = reinterpret_cast<tuple_expr*>(value_node)
->get_elements(); ->get_elements();
for(i32 i = size-1; i>=0; --i) { for(i64 i = size-1; i>=0; --i) {
calc_gen(value_tuple[i]); calc_gen(value_tuple[i]);
} }
auto& tuple = tuple_node->get_elements(); auto& tuple = tuple_node->get_elements();
for(i32 i = 0; i<size; ++i) { for(i64 i = 0; i<size; ++i) {
mcall(tuple[i]); mcall(tuple[i]);
// use load operands to avoid meq's pop operand // use load operands to avoid meq's pop operand
// and this operation changes local and global value directly // and this operation changes local and global value directly
@ -773,13 +785,14 @@ void codegen::multi_assign_gen(multi_assign* node) {
// generate multiple assignment: (a, b, c) = [1, 2, 3]; // generate multiple assignment: (a, b, c) = [1, 2, 3];
calc_gen(value_node); calc_gen(value_node);
auto& tuple = tuple_node->get_elements(); auto& tuple = tuple_node->get_elements();
for(i32 i = 0; i<size; ++i) { for(i64 i = 0; i<size; ++i) {
emit(op_callvi, i, value_node->get_location()); emit(op_callvi, i, value_node->get_location());
mcall(tuple[i]); mcall(tuple[i]);
// use load operands to avoid meq's pop operand // use load operands to avoid meq's pop operand
// and this operation changes local and global value directly // and this operation changes local and global value directly
replace_left_assignment_with_load(tuple[i]->get_location()); replace_left_assignment_with_load(tuple[i]->get_location());
} }
// pop source vector // pop source vector
emit(op_pop, 0, node->get_location()); emit(op_pop, 0, node->get_location());
} }
@ -834,7 +847,7 @@ void codegen::loop_gen(expr* node) {
} }
} }
void codegen::load_continue_break(i32 continue_place, i32 break_place) { void codegen::load_continue_break(u64 continue_place, u64 break_place) {
for(auto i : continue_ptr.front()) { for(auto i : continue_ptr.front()) {
code[i].num = continue_place; code[i].num = continue_place;
} }
@ -943,7 +956,7 @@ void codegen::statement_generation(expr* node) {
case expr_type::ast_ternary: case expr_type::ast_ternary:
calc_gen(node); calc_gen(node);
// only generate in repl mode and in global scope // only generate in repl mode and in global scope
if (need_repl_output && local.empty()) { if (flag_need_repl_output && local.empty()) {
emit(op_repl, 0, node->get_location()); emit(op_repl, 0, node->get_location());
} }
emit(op_pop, 0, node->get_location()); emit(op_pop, 0, node->get_location());
@ -1254,7 +1267,7 @@ void codegen::block_gen(code_block* node) {
break; break;
case expr_type::ast_null: break; case expr_type::ast_null: break;
case expr_type::ast_id: case expr_type::ast_id:
if (need_repl_output && local.empty()) { if (flag_need_repl_output && local.empty()) {
repl_mode_info_output_gen(tmp); repl_mode_info_output_gen(tmp);
} else { } else {
check_id_exist(reinterpret_cast<identifier*>(tmp)); check_id_exist(reinterpret_cast<identifier*>(tmp));
@ -1264,7 +1277,7 @@ void codegen::block_gen(code_block* node) {
case expr_type::ast_num: case expr_type::ast_num:
case expr_type::ast_str: case expr_type::ast_str:
case expr_type::ast_bool: case expr_type::ast_bool:
if (need_repl_output && local.empty()) { if (flag_need_repl_output && local.empty()) {
repl_mode_info_output_gen(tmp); repl_mode_info_output_gen(tmp);
} }
break; break;
@ -1313,7 +1326,7 @@ const error& codegen::compile(parse& parse,
linker& import, linker& import,
bool repl_flag, bool repl_flag,
bool limit_mode) { bool limit_mode) {
need_repl_output = repl_flag; flag_need_repl_output = repl_flag;
flag_limited_mode = limit_mode; flag_limited_mode = limit_mode;
init_native_function(); init_native_function();
init_file_map(import.get_file_list()); init_file_map(import.get_file_list());
@ -1334,13 +1347,13 @@ const error& codegen::compile(parse& parse,
emit(op_exit, 0, parse.tree()->get_location()); emit(op_exit, 0, parse.tree()->get_location());
// size out of bound check // size out of bound check
if (const_number_table.size()>0xffffff) { if (const_number_table.size()>INT64_MAX) {
err.err("code", err.err("code",
"too many constant numbers: " + "too many constant numbers: " +
std::to_string(const_number_table.size()) std::to_string(const_number_table.size())
); );
} }
if (const_string_table.size()>0xffffff) { if (const_string_table.size()>INT64_MAX) {
err.err("code", err.err("code",
"too many constant strings: " + "too many constant strings: " +
std::to_string(const_string_table.size()) std::to_string(const_string_table.size())
@ -1348,7 +1361,7 @@ const error& codegen::compile(parse& parse,
} }
// check global variables size // check global variables size
if (global.size()>=STACK_DEPTH/2) { if (global.size()>=VM_STACK_DEPTH) {
err.err("code", err.err("code",
"too many global variables: " + "too many global variables: " +
std::to_string(global.size()) std::to_string(global.size())
@ -1356,7 +1369,7 @@ const error& codegen::compile(parse& parse,
} }
// check generated code size // check generated code size
if (code.size()>0xffffff) { if (code.size()>INT64_MAX) {
err.err("code", err.err("code",
"bytecode size overflow: " + "bytecode size overflow: " +
std::to_string(code.size()) std::to_string(code.size())
@ -1367,8 +1380,8 @@ const error& codegen::compile(parse& parse,
void codegen::print(std::ostream& out) { void codegen::print(std::ostream& out) {
// func end stack, reserved for code print // func end stack, reserved for code print
std::stack<u32> func_begin_stack; std::stack<u64> func_begin_stack;
std::stack<u32> func_end_stack; std::stack<u64> func_end_stack;
// print const numbers // print const numbers
for(auto num : const_number_table) { for(auto num : const_number_table) {
@ -1391,7 +1404,8 @@ void codegen::print(std::ostream& out) {
const_string_table.data(), const_string_table.data(),
native_function.data() native_function.data()
); );
for(u32 i = 0; i<code.size(); ++i) {
for(u64 i = 0; i<code.size(); ++i) {
// print opcode index, opcode name, opcode immediate number // print opcode index, opcode name, opcode immediate number
const auto& c = code[i]; const auto& c = code[i];
if (!func_end_stack.empty() && i==func_end_stack.top()) { if (!func_end_stack.empty() && i==func_end_stack.top()) {
@ -1407,7 +1421,7 @@ void codegen::print(std::ostream& out) {
// get function begin index and end index // get function begin index and end index
if (c.op==op_newf) { if (c.op==op_newf) {
out << std::hex << "\nfunc <0x" << i << std::dec << ">:\n"; out << std::hex << "\nfunc <0x" << i << std::dec << ">:\n";
for(u32 j = i; j<code.size(); ++j) { for(u64 j = i; j<code.size(); ++j) {
if (code[j].op==op_jmp) { if (code[j].op==op_jmp) {
func_begin_stack.push(i); func_begin_stack.push(i);
func_end_stack.push(code[j].num); func_end_stack.push(code[j].num);
@ -1422,7 +1436,7 @@ void codegen::print(std::ostream& out) {
} }
void codegen::symbol_dump(std::ostream& out) const { void codegen::symbol_dump(std::ostream& out) const {
for(const auto& domain : experimental_namespace) { for(const auto& domain : nasal_namespace) {
out << "<" << domain.first << ">\n"; out << "<" << domain.first << ">\n";
for(const auto& i : domain.second) { for(const auto& i : domain.second) {
out << " 0x" << std::setw(4) << std::setfill('0'); out << " 0x" << std::setw(4) << std::setfill('0');

View File

@ -37,10 +37,11 @@ private:
error err; error err;
// repl output flag, will generate op_repl to output stack top value if true // repl output flag, will generate op_repl to output stack top value if true
bool need_repl_output = false; bool flag_need_repl_output = false;
// limit mode flag // limit mode flag
bool flag_limited_mode = false; bool flag_limited_mode = false;
// under limited mode, unsafe system api will be banned // under limited mode, unsafe system api will be banned
const std::unordered_set<std::string> unsafe_system_api = { const std::unordered_set<std::string> unsafe_system_api = {
// builtin // builtin
@ -67,8 +68,8 @@ private:
std::vector<u32> in_foreach_loop_level; std::vector<u32> in_foreach_loop_level;
// constant numbers and strings // constant numbers and strings
std::unordered_map<f64, u32> const_number_map; std::unordered_map<f64, u64> const_number_map;
std::unordered_map<std::string, u32> const_string_map; std::unordered_map<std::string, u64> const_string_map;
std::vector<f64> const_number_table; std::vector<f64> const_number_table;
std::vector<std::string> const_string_table; std::vector<std::string> const_string_table;
@ -82,17 +83,20 @@ private:
std::vector<opcode> code; std::vector<opcode> code;
// used to store jmp operands index, to fill the jump address back // used to store jmp operands index, to fill the jump address back
std::list<std::vector<i32>> continue_ptr; std::list<std::vector<u64>> continue_ptr;
std::list<std::vector<i32>> break_ptr; std::list<std::vector<u64>> break_ptr;
// symbol table // symbol table
// global : max STACK_DEPTH-1 values // global : max VM_STACK_DEPTH-1 values
std::unordered_map<std::string, i32> global; std::unordered_map<std::string, u64> global;
std::unordered_map<std::string, std::unordered_set<std::string>> experimental_namespace;
// nasal namespace
// stores all global symbols of each file
std::unordered_map<std::string, std::unordered_set<std::string>> nasal_namespace;
// local : max 32768 upvalues 65536 values // local : max 32768 upvalues 65536 values
// but in fact local scope also has less than STACK_DEPTH value // but in fact local scope also has less than VM_STACK_DEPTH value
std::list<std::unordered_map<std::string, i32>> local; std::list<std::unordered_map<std::string, u64>> local;
void check_id_exist(identifier*); void check_id_exist(identifier*);
@ -104,11 +108,11 @@ private:
void regist_string(const std::string&); void regist_string(const std::string&);
void find_symbol(code_block*); void find_symbol(code_block*);
void regist_symbol(const std::string&); void regist_symbol(const std::string&);
i32 local_symbol_find(const std::string&); i64 local_symbol_find(const std::string&);
i32 global_symbol_find(const std::string&); i64 global_symbol_find(const std::string&);
i32 upvalue_symbol_find(const std::string&); i64 upvalue_symbol_find(const std::string&);
void emit(u8, u32, const span&); void emit(u8, u64, const span&);
void number_gen(number_literal*); void number_gen(number_literal*);
void string_gen(string_literal*); void string_gen(string_literal*);
@ -135,7 +139,7 @@ private:
void multi_assign_gen(multi_assign*); void multi_assign_gen(multi_assign*);
void cond_gen(condition_expr*); void cond_gen(condition_expr*);
void loop_gen(expr*); void loop_gen(expr*);
void load_continue_break(i32, i32); void load_continue_break(u64, u64);
void while_gen(while_expr*); void while_gen(while_expr*);
void for_gen(for_expr*); void for_gen(for_expr*);
void forei_gen(forei_expr*); void forei_gen(forei_expr*);
@ -156,9 +160,6 @@ public:
const auto& natives() const {return native_function;} const auto& natives() const {return native_function;}
const auto& codes() const {return code;} const auto& codes() const {return code;}
const auto& globals() const {return global;} const auto& globals() const {return global;}
const auto& get_experimental_namespace() const {
return experimental_namespace;
}
public: public:
codegen() = default; codegen() = default;

View File

@ -46,7 +46,7 @@ void operand_line_counter::dump_operand_count() const {
if (!rate) { if (!rate) {
break; break;
} }
std::clog << " " << opname[i.first] << " : "; std::clog << " " << oprand_name_table[i.first] << " : ";
std::clog << i.second << " (" << rate << "%)\n"; std::clog << i.second << " (" << rate << "%)\n";
} }
std::clog << " total : " << total << '\n'; std::clog << " total : " << total << '\n';
@ -108,21 +108,21 @@ std::vector<std::string> dbg::parse(const std::string& cmd) {
} }
u16 dbg::file_index(const std::string& filename) const { u16 dbg::file_index(const std::string& filename) const {
for(u16 i = 0; i<fsize; ++i) { for(u16 i = 0; i<file_list_size; ++i) {
if (filename==files[i]) { if (filename==files[i]) {
return i; return i;
} }
} }
return 65535; return UINT16_MAX;
} }
void dbg::err() { void dbg::err() const {
std::cerr std::cerr
<< "incorrect command\n" << "incorrect command\n"
<< "input \'h\' to get help\n"; << "input \'h\' to get help\n";
} }
void dbg::help() { void dbg::help() const {
std::clog std::clog
<< "<option>\n" << "<option>\n"
<< " h, help | get help\n" << " h, help | get help\n"
@ -133,7 +133,7 @@ void dbg::help() {
<< " l, local | see local values\n" << " l, local | see local values\n"
<< " u, upval | see upvalue\n" << " u, upval | see upvalue\n"
<< " r, register | show vm register detail\n" << " r, register | show vm register detail\n"
<< " a, all | show global,local and upvalue\n" << " a, all | show global, local and upvalue\n"
<< " n, next | execute next bytecode\n" << " n, next | execute next bytecode\n"
<< " q, exit | exit debugger\n" << " q, exit | exit debugger\n"
<< "<option> <filename> <line>\n" << "<option> <filename> <line>\n"
@ -141,18 +141,20 @@ void dbg::help() {
} }
void dbg::list_file() const { void dbg::list_file() const {
for(usize i = 0; i<fsize; ++i) { for(usize i = 0; i<file_list_size; ++i) {
std::clog << "[" << i << "] " << files[i] << "\n"; std::clog << "[" << i << "] " << files[i] << "\n";
} }
} }
void dbg::step_info() { void dbg::step_info() {
u32 line = bytecode[ctx.pc].line==0? 0:bytecode[ctx.pc].line-1; u64 line = bytecode[ctx.pc].line==0? 0:bytecode[ctx.pc].line-1;
u32 begin = (line>>3)==0? 0:((line>>3)<<3); u64 begin = (line>>3)==0? 0:((line>>3)<<3);
u32 end = (1+(line>>3))<<3; u64 end = (1+(line>>3))<<3;
src.load(files[bytecode[ctx.pc].fidx]); src.load(files[bytecode[ctx.pc].fidx]);
std::clog << "\nsource code:\n"; std::clog << "\nsource code:\n";
for(u32 i = begin; i<end && i<src.size(); ++i) { for(u64 i = begin; i<end && i<src.size(); ++i) {
std::clog << (i==line? back_white:reset); std::clog << (i==line? back_white:reset);
std::clog << (i==line? "--> ":" ") << src[i] << reset << "\n"; std::clog << (i==line? "--> ":" ") << src[i] << reset << "\n";
} }
@ -160,15 +162,16 @@ void dbg::step_info() {
begin = (ctx.pc>>3)==0? 0:((ctx.pc>>3)<<3); begin = (ctx.pc>>3)==0? 0:((ctx.pc>>3)<<3);
end = (1+(ctx.pc>>3))<<3; end = (1+(ctx.pc>>3))<<3;
codestream::set(const_number, const_string, native_function.data(), files); codestream::set(const_number, const_string, native_function.data(), files);
std::clog << "\nnext bytecode:\n"; std::clog << "\nnext bytecode:\n";
for(u32 i = begin; i<end && bytecode[i].op!=op_exit; ++i) { for(u64 i = begin; i<end && bytecode[i].op!=op_exit; ++i) {
std::clog std::clog
<< (i==ctx.pc? back_white:reset) << (i==ctx.pc? back_white:reset)
<< (i==ctx.pc? "--> ":" ") << (i==ctx.pc? "--> ":" ")
<< codestream(bytecode[i], i) << codestream(bytecode[i], i)
<< reset << "\n"; << reset << "\n";
} }
stack_info(10); stack_info(16);
} }
void dbg::interact() { void dbg::interact() {
@ -218,7 +221,7 @@ void dbg::interact() {
} else if (res.size()==3 && } else if (res.size()==3 &&
get_cmd_type(res[0])==cmd_kind::cmd_break_point) { get_cmd_type(res[0])==cmd_kind::cmd_break_point) {
break_file_index = file_index(res[1]); break_file_index = file_index(res[1]);
if (break_file_index==65535) { if (break_file_index==UINT16_MAX) {
std::clog << "cannot find file named `" << res[1] << "`\n"; std::clog << "cannot find file named `" << res[1] << "`\n";
continue; continue;
} }
@ -234,8 +237,7 @@ void dbg::interact() {
} }
} }
void dbg::run( void dbg::run(const codegen& gen,
const codegen& gen,
const linker& linker, const linker& linker,
const std::vector<std::string>& argv, const std::vector<std::string>& argv,
bool profile, bool profile,
@ -245,8 +247,9 @@ void dbg::run(
do_operand_count = profile || show_all_prof_result; do_operand_count = profile || show_all_prof_result;
const auto& file_list = linker.get_file_list(); const auto& file_list = linker.get_file_list();
fsize = file_list.size(); file_list_size = file_list.size();
init(
vm_init_enrty(
gen.strs(), gen.strs(),
gen.nums(), gen.nums(),
gen.natives(), gen.natives(),
@ -257,10 +260,10 @@ void dbg::run(
); );
counter.init(file_list); counter.init(file_list);
std::vector<u32> code; std::vector<u8> code;
std::vector<u16> code_file_index; std::vector<u16> code_file_index;
std::vector<u32> code_line; std::vector<u64> code_line;
for(auto& i : gen.codes()) { for(const auto& i : gen.codes()) {
code.push_back(i.op); code.push_back(i.op);
code_file_index.push_back(i.fidx); code_file_index.push_back(i.fidx);
code_line.push_back(i.line); code_line.push_back(i.line);

View File

@ -140,9 +140,9 @@ private:
private: private:
bool next; bool next;
usize fsize; usize file_list_size;
u16 break_file_index; u16 break_file_index;
u32 break_line; u64 break_line;
error src; error src;
private: private:
@ -152,24 +152,21 @@ private:
private: private:
std::vector<std::string> parse(const std::string&); std::vector<std::string> parse(const std::string&);
u16 file_index(const std::string&) const; u16 file_index(const std::string&) const;
void err(); void err() const;
void help(); void help() const;
void list_file() const; void list_file() const;
void step_info(); void step_info();
void interact(); void interact();
public: public:
dbg(): dbg(): next(true), file_list_size(0),
next(true), fsize(0),
break_file_index(0), break_line(0), break_file_index(0), break_line(0),
do_operand_count(false) {} do_operand_count(false) {}
void run( void run(const codegen&,
const codegen&,
const linker&, const linker&,
const std::vector<std::string>&, const std::vector<std::string>&,
bool, bool,
bool bool);
);
}; };
} }

View File

@ -120,22 +120,23 @@ void error::warn(const std::string& stage, const std::string& info) {
std::clog << orange << stage << ": " << white << info << reset << "\n\n"; std::clog << orange << stage << ": " << white << info << reset << "\n\n";
} }
void error::err( void error::err(const std::string& stage,
const std::string& stage, const span& loc, const std::string& info) { const span& loc,
const std::string& info) {
// load error occurred file into string lines // load error occurred file into string lines
load(loc.file); load(loc.file);
++cnt; ++cnt;
std::cerr std::cerr << red << stage << ": " << white << info << reset << "\n";
<< red << stage << ": " << white << info << reset << "\n" << cyan << " --> " std::cerr << cyan << " --> " << red;
<< red << loc.file << ":" << loc.begin_line << ":" << loc.begin_column+1 loc.dump_begin(std::cerr);
<< reset << "\n"; std::cerr << reset << "\n";
const usize maxlen = std::to_string(loc.end_line).length(); const usize maxlen = std::to_string(loc.end_line).length();
const std::string iden = identation(maxlen); const std::string iden = identation(maxlen);
for(u32 line = loc.begin_line; line<=loc.end_line; ++line) { for(u64 line = loc.begin_line; line<=loc.end_line; ++line) {
// skip line 0 // skip line 0
if (!line) { if (!line) {
continue; continue;
@ -164,25 +165,25 @@ void error::err(
// output underline // output underline
std::cerr << cyan << iden << " | " << reset; std::cerr << cyan << iden << " | " << reset;
if (loc.begin_line==loc.end_line) { if (loc.begin_line==loc.end_line) {
for(u32 i = 0; i<loc.begin_column; ++i) { for(u64 i = 0; i<loc.begin_column; ++i) {
std::cerr << char(" \t"[code[i]=='\t']); std::cerr << char(" \t"[code[i]=='\t']);
} }
for(u32 i = loc.begin_column; i<loc.end_column; ++i) { for(u64 i = loc.begin_column; i<loc.end_column; ++i) {
std::cerr << red << (code[i]=='\t'? "^^^^":"^") << reset; std::cerr << red << (code[i]=='\t'? "^^^^":"^") << reset;
} }
} else if (line==loc.begin_line) { } else if (line==loc.begin_line) {
for(u32 i = 0; i<loc.begin_column; ++i) { for(u64 i = 0; i<loc.begin_column; ++i) {
std::cerr << char(" \t"[code[i]=='\t']); std::cerr << char(" \t"[code[i]=='\t']);
} }
for(u32 i = loc.begin_column; i<code.size(); ++i) { for(u64 i = loc.begin_column; i<code.size(); ++i) {
std::cerr << red << (code[i]=='\t'? "^^^^":"^") << reset; std::cerr << red << (code[i]=='\t'? "^^^^":"^") << reset;
} }
} else if (loc.begin_line<line && line<loc.end_line) { } else if (loc.begin_line<line && line<loc.end_line) {
for(u32 i = 0; i<code.size(); ++i) { for(u64 i = 0; i<code.size(); ++i) {
std::cerr << red << (code[i]=='\t'? "^^^^":"^"); std::cerr << red << (code[i]=='\t'? "^^^^":"^");
} }
} else { } else {
for(u32 i = 0; i<loc.end_column; ++i) { for(u64 i = 0; i<loc.end_column; ++i) {
std::cerr << red << (code[i]=='\t'? "^^^^":"^"); std::cerr << red << (code[i]=='\t'? "^^^^":"^");
} }
} }

View File

@ -11,11 +11,15 @@
namespace nasal { namespace nasal {
struct span { struct span {
u32 begin_line; u64 begin_line;
u32 begin_column; u64 begin_column;
u32 end_line; u64 end_line;
u32 end_column; u64 end_column;
std::string file; std::string file;
void dump_begin(std::ostream& out) const {
out << file << ":" << begin_line << ":" << begin_column + 1;
}
}; };
std::ostream& back_white(std::ostream&); std::ostream& back_white(std::ostream&);
@ -46,7 +50,7 @@ private:
std::string identation(usize len) { std::string identation(usize len) {
return std::string(len, ' '); return std::string(len, ' ');
} }
std::string leftpad(u32 num, usize len) { std::string leftpad(u64 num, usize len) {
auto tmp = std::to_string(num); auto tmp = std::to_string(num);
while(tmp.length()<len) { while(tmp.length()<len) {
tmp = " "+tmp; tmp = " "+tmp;
@ -65,7 +69,7 @@ public:
std::exit(1); std::exit(1);
} }
} }
u32 geterr() const {return cnt;} auto geterr() const { return cnt; }
}; };
} }

View File

@ -22,7 +22,10 @@ void gc::do_mark_sweep() {
void gc::mark() { void gc::mark() {
std::vector<var> bfs; std::vector<var> bfs;
mark_context_root(bfs); mark_context_root(bfs);
if (memory.size()>8192 && bfs.size()>4) {
// concurrent mark, experimental
if (memory.size()>UINT16_MAX && bfs.size()>32) {
flag_concurrent_mark_triggered = true;
usize size = bfs.size(); usize size = bfs.size();
std::thread t0(&gc::concurrent_mark, this, std::ref(bfs), 0, size/4); std::thread t0(&gc::concurrent_mark, this, std::ref(bfs), 0, size/4);
std::thread t1(&gc::concurrent_mark, this, std::ref(bfs), size/4, size/2); std::thread t1(&gc::concurrent_mark, this, std::ref(bfs), size/4, size/2);
@ -35,6 +38,7 @@ void gc::mark() {
return; return;
} }
// normal mark
while(!bfs.empty()) { while(!bfs.empty()) {
var value = bfs.back(); var value = bfs.back();
bfs.pop_back(); bfs.pop_back();
@ -189,7 +193,7 @@ void gc::extend(const vm_type type) {
const u8 index = static_cast<u8>(type)-static_cast<u8>(vm_type::vm_str); const u8 index = static_cast<u8>(type)-static_cast<u8>(vm_type::vm_str);
size[index] += incr[index]; size[index] += incr[index];
for(u32 i = 0; i<incr[index]; ++i) { for(u64 i = 0; i<incr[index]; ++i) {
// no need to check, will be killed if memory is not enough // no need to check, will be killed if memory is not enough
nas_val* tmp = new nas_val(type); nas_val* tmp = new nas_val(type);
@ -197,14 +201,13 @@ void gc::extend(const vm_type type) {
memory.push_back(tmp); memory.push_back(tmp);
unused[index].push_back(tmp); unused[index].push_back(tmp);
} }
// if incr[index] = 1, this will always be 1 // if incr[index] = 1, this will always be 1
incr[index] = incr[index]+incr[index]/2; incr[index] = incr[index]+incr[index]/2;
} }
void gc::init( void gc::init(const std::vector<std::string>& constant_strings,
const std::vector<std::string>& constant_strings, const std::vector<std::string>& argv) {
const std::vector<std::string>& argv
) {
// initialize counters // initialize counters
worktime = 0; worktime = 0;
for(u8 i = 0; i<gc_type_size; ++i) { for(u8 i = 0; i<gc_type_size; ++i) {
@ -216,25 +219,25 @@ void gc::init(
// init constant strings // init constant strings
strs.resize(constant_strings.size()); strs.resize(constant_strings.size());
for(u32 i = 0; i<strs.size(); ++i) { for(u64 i = 0; i<strs.size(); ++i) {
// incremental initialization, avoid memory leak in repl mode // incremental initialization, avoid memory leak in repl mode
if (strs[i].is_str() && strs[i].str()==constant_strings[i]) { if (strs[i].is_str() && strs[i].str()==constant_strings[i]) {
continue; continue;
} }
strs[i] = var::gcobj(new nas_val(vm_type::vm_str)); strs[i] = var::gcobj(new nas_val(vm_type::vm_str));
strs[i].val.gcobj->unmutable = 1; strs[i].val.gcobj->immutable = 1;
strs[i].str() = constant_strings[i]; strs[i].str() = constant_strings[i];
} }
// record arguments // record arguments
env_argv.resize(argv.size()); env_argv.resize(argv.size());
for(usize i = 0; i<argv.size(); ++i) { for(u64 i = 0; i<argv.size(); ++i) {
// incremental initialization, avoid memory leak in repl mode // incremental initialization, avoid memory leak in repl mode
if (env_argv[i].is_str() && env_argv[i].str()==argv[i]) { if (env_argv[i].is_str() && env_argv[i].str()==argv[i]) {
continue; continue;
} }
env_argv[i] = var::gcobj(new nas_val(vm_type::vm_str)); env_argv[i] = var::gcobj(new nas_val(vm_type::vm_str));
env_argv[i].val.gcobj->unmutable = 1; env_argv[i].val.gcobj->immutable = 1;
env_argv[i].str() = argv[i]; env_argv[i].str() = argv[i];
} }
} }
@ -315,7 +318,7 @@ void gc::info() const {
if (!gcnt[i] && !acnt[i] && !size[i]) { if (!gcnt[i] && !acnt[i] && !size[i]) {
continue; continue;
} }
total += gcnt[i]; total += static_cast<f64>(gcnt[i]);
std::clog << " " << left << setw(indent) << setfill(' ') << name[i]; std::clog << " " << left << setw(indent) << setfill(' ') << name[i];
std::clog << " | " << left << setw(indent) << setfill(' ') << gcnt[i]; std::clog << " | " << left << setw(indent) << setfill(' ') << gcnt[i];
std::clog << " | " << left << setw(indent) << setfill(' ') << acnt[i]; std::clog << " | " << left << setw(indent) << setfill(' ') << acnt[i];
@ -341,6 +344,8 @@ void gc::info() const {
std::clog << " | " << max_mark_time*1.0/den*1000 << " ms\n"; std::clog << " | " << max_mark_time*1.0/den*1000 << " ms\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "max sweep"; std::clog << " " << left << setw(indent) << setfill(' ') << "max sweep";
std::clog << " | " << max_sweep_time*1.0/den*1000 << " ms\n"; std::clog << " | " << max_sweep_time*1.0/den*1000 << " ms\n";
std::clog << " " << left << setw(indent) << setfill(' ') << "concurrent";
std::clog << " | " << (flag_concurrent_mark_triggered? "true\n":"false\n");
std::clog << last_line << "\n"; std::clog << last_line << "\n";
} }

View File

@ -41,7 +41,7 @@ struct gc {
std::vector<nas_val*> unused[gc_type_size]; // gc free list std::vector<nas_val*> unused[gc_type_size]; // gc free list
/* heap increase size */ /* heap increase size */
u32 incr[gc_type_size] = { u64 incr[gc_type_size] = {
128, // vm_str 128, // vm_str
128, // vm_vec 128, // vm_vec
64, // vm_hash 64, // vm_hash
@ -60,6 +60,7 @@ struct gc {
i64 max_time = 0; i64 max_time = 0;
i64 max_mark_time = 0; i64 max_mark_time = 0;
i64 max_sweep_time = 0; i64 max_sweep_time = 0;
bool flag_concurrent_mark_triggered = false;
void set(context* _ctx, var* _global, usize _size) { void set(context* _ctx, var* _global, usize _size) {
running_context = _ctx; running_context = _ctx;
@ -115,7 +116,7 @@ public:
// module function type // module function type
typedef var (*module_func)(var*, usize, gc*); typedef var (*module_func)(var*, usize, gc*);
// module function stores in tables with this type, end with {nullptr,nullptr} // module function stores in tables with this type, end with {nullptr, nullptr}
struct module_func_info { struct module_func_info {
const char* name; const char* name;
module_func fd; module_func fd;

View File

@ -41,8 +41,8 @@ std::string linker::get_path(expr* node) {
return content->get_content(); return content->get_content();
} }
std::string linker::find_real_file_path( std::string linker::find_real_file_path(const std::string& filename,
const std::string& filename, const span& location) { const span& location) {
// first add file name itself into the file path // first add file name itself into the file path
std::vector<fs::path> path_list = {filename}; std::vector<fs::path> path_list = {filename};
@ -316,7 +316,7 @@ std::string linker::generate_module_name(const std::string& file_path) {
} }
return_expr* linker::generate_module_return(code_block* block) { return_expr* linker::generate_module_return(code_block* block) {
auto finder = std::unique_ptr<symbol_finder>(new symbol_finder); auto finder = std::make_unique<symbol_finder>();
auto result = new return_expr(block->get_location()); auto result = new return_expr(block->get_location());
auto value = new hash_expr(block->get_location()); auto value = new hash_expr(block->get_location());
result->set_value(value); result->set_value(value);
@ -406,6 +406,13 @@ const error& linker::link(parse& parse, bool spath = false) {
merge_tree(library, parse.tree()); merge_tree(library, parse.tree());
// swap tree root, and delete old root // swap tree root, and delete old root
delete parse.swap(library); delete parse.swap(library);
if (imported_files.size()>=UINT16_MAX) {
err.err("link",
"too many imported files: " +
std::to_string(imported_files.size())
);
}
return err; return err;
} }

View File

@ -96,7 +96,7 @@ void lexer::open(const std::string& file) {
} }
tok lexer::get_type(const std::string& str) { tok lexer::get_type(const std::string& str) {
return typetbl.count(str)? typetbl.at(str):tok::null; return token_mapper.count(str)? token_mapper.at(str):tok::null;
} }
std::string lexer::utf8_gen() { std::string lexer::utf8_gen() {
@ -138,8 +138,8 @@ std::string lexer::utf8_gen() {
} }
token lexer::id_gen() { token lexer::id_gen() {
u32 begin_line = line; u64 begin_line = line;
u32 begin_column = column; u64 begin_column = column;
std::string str = ""; std::string str = "";
while(ptr<res.size() && (is_id(res[ptr]) || is_dec(res[ptr]))) { while(ptr<res.size() && (is_id(res[ptr]) || is_dec(res[ptr]))) {
if (res[ptr]<0) { // utf-8 if (res[ptr]<0) { // utf-8
@ -157,8 +157,8 @@ token lexer::id_gen() {
} }
token lexer::num_gen() { token lexer::num_gen() {
u32 begin_line = line; u64 begin_line = line;
u32 begin_column = column; u64 begin_column = column;
// generate hex number // generate hex number
if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='x') { if (ptr+1<res.size() && res[ptr]=='0' && res[ptr+1]=='x') {
std::string str = "0x"; std::string str = "0x";
@ -239,8 +239,8 @@ token lexer::num_gen() {
} }
token lexer::str_gen() { token lexer::str_gen() {
u32 begin_line = line; u64 begin_line = line;
u32 begin_column = column; u64 begin_column = column;
std::string str = ""; std::string str = "";
const char begin = res[ptr]; const char begin = res[ptr];
++column; ++column;
@ -298,8 +298,8 @@ token lexer::str_gen() {
} }
token lexer::single_opr() { token lexer::single_opr() {
u32 begin_line = line; u64 begin_line = line;
u32 begin_column = column; u64 begin_column = column;
std::string str(1, res[ptr]); std::string str(1, res[ptr]);
++column; ++column;
tok type = get_type(str); tok type = get_type(str);
@ -314,8 +314,8 @@ token lexer::single_opr() {
} }
token lexer::dots() { token lexer::dots() {
u32 begin_line = line; u64 begin_line = line;
u32 begin_column = column; u64 begin_column = column;
std::string str = "."; std::string str = ".";
if (ptr+2<res.size() && res[ptr+1]=='.' && res[ptr+2]=='.') { if (ptr+2<res.size() && res[ptr+1]=='.' && res[ptr+2]=='.') {
str += ".."; str += "..";
@ -326,8 +326,8 @@ token lexer::dots() {
} }
token lexer::calc_opr() { token lexer::calc_opr() {
u32 begin_line = line; u64 begin_line = line;
u32 begin_column = column; u64 begin_column = column;
// get calculation operator // get calculation operator
std::string str(1, res[ptr++]); std::string str(1, res[ptr++]);
if (ptr<res.size() && res[ptr]=='=') { if (ptr<res.size() && res[ptr]=='=') {

View File

@ -36,7 +36,7 @@ enum class tok:u32 {
rif, // condition expression keyword if rif, // condition expression keyword if
elsif, // condition expression keyword elsif elsif, // condition expression keyword elsif
relse, // condition expression keyword else relse, // condition expression keyword else
tknil, // nil literal nil, // nil literal
lcurve, // ( lcurve, // (
rcurve, // ) rcurve, // )
lbracket, // [ lbracket, // [
@ -88,8 +88,8 @@ struct token {
class lexer { class lexer {
private: private:
u32 line; u64 line;
u32 column; u64 column;
usize ptr; usize ptr;
std::string filename; std::string filename;
std::string res; std::string res;
@ -98,7 +98,7 @@ private:
u64 invalid_char; u64 invalid_char;
std::vector<token> toks; std::vector<token> toks;
const std::unordered_map<std::string, tok> typetbl { const std::unordered_map<std::string, tok> token_mapper = {
{"use" ,tok::use }, {"use" ,tok::use },
{"true" ,tok::tktrue }, {"true" ,tok::tktrue },
{"false" ,tok::tkfalse }, {"false" ,tok::tkfalse },
@ -114,7 +114,7 @@ private:
{"if" ,tok::rif }, {"if" ,tok::rif },
{"elsif" ,tok::elsif }, {"elsif" ,tok::elsif },
{"else" ,tok::relse }, {"else" ,tok::relse },
{"nil" ,tok::tknil }, {"nil" ,tok::nil },
{"(" ,tok::lcurve }, {"(" ,tok::lcurve },
{")" ,tok::rcurve }, {")" ,tok::rcurve },
{"[" ,tok::lbracket}, {"[" ,tok::lbracket},
@ -177,7 +177,8 @@ private:
token dots(); token dots();
token calc_opr(); token calc_opr();
public: public:
lexer(): line(1), column(0), ptr(0), filename(""), res(""), invalid_char(0) {} lexer(): line(1), column(0), ptr(0),
filename(""), res(""), invalid_char(0) {}
const error& scan(const std::string&); const error& scan(const std::string&);
const std::vector<token>& result() const {return toks;} const std::vector<token>& result() const {return toks;}
}; };

View File

@ -2,7 +2,7 @@
namespace nasal { namespace nasal {
const char* opname[] = { const char* oprand_name_table[] = {
"exit ", "repl ", "intl ", "loadg ", "exit ", "repl ", "intl ", "loadg ",
"loadl ", "loadu ", "pnum ", "pnil ", "loadl ", "loadu ", "pnum ", "pnil ",
"pstr ", "newv ", "newh ", "newf ", "pstr ", "newv ", "newh ", "newf ",
@ -27,8 +27,7 @@ const char* opname[] = {
"mcallv", "mcallh", "ret " "mcallv", "mcallh", "ret "
}; };
void codestream::set( void codestream::set(const f64* number_list,
const f64* number_list,
const std::string* string_list, const std::string* string_list,
const nasal_builtin_table* native_table, const nasal_builtin_table* native_table,
const std::string* file_list) { const std::string* file_list) {
@ -43,73 +42,115 @@ void codestream::dump(std::ostream& out) const {
using std::setfill; using std::setfill;
using std::hex; using std::hex;
using std::dec; using std::dec;
auto op = code.op;
auto num = code.num; const auto op = code.op;
const auto num = code.num;
// dump operand index and bytecode(hex format)
out << hex << "0x" out << hex << "0x"
<< setw(6) << setfill('0') << index << " " << setw(6) << setfill('0') << index << " "
<< setw(2) << setfill('0') << static_cast<u32>(op) << " " << setw(2) << setfill('0') << static_cast<u32>(op) << ":" << dec;
<< setw(2) << setfill('0') << ((num>>16)&0xff) << " "
<< setw(2) << setfill('0') << ((num>>8)&0xff) << " " // dump immediate number(hex format)
<< setw(2) << setfill('0') << (num&0xff) << " " for(i32 i = 64-8; i>=0; i -= 8) {
<<opname[op]<<" "<<dec; out << hex << setw(2) << setfill('0') << ((num>>i)&0xff) << dec << " ";
}
// dump operand name
out << " " << oprand_name_table[op] << " ";
switch(op) { switch(op) {
case op_addeq: case op_subeq: case op_addeq:
case op_muleq: case op_diveq: case op_subeq:
case op_lnkeq: case op_meq: case op_muleq:
case op_btandeq: case op_btoreq: case op_diveq:
case op_lnkeq:
case op_meq:
case op_btandeq:
case op_btoreq:
case op_btxoreq: case op_btxoreq:
out << hex << "0x" << num << dec << " sp-" << num; break; out << hex << "0x" << num << dec << " sp-" << num;
case op_addeqc: case op_subeqc: break;
case op_muleqc:case op_diveqc: case op_addeqc:
out << hex << "0x" << num << dec case op_subeqc:
<< " (" << const_number[num] << ")"; break; case op_muleqc:
case op_diveqc:
out << hex << "0x" << num << dec;
out << " (" << const_number[num] << ")";
break;
case op_lnkeqc: case op_lnkeqc:
out << hex << "0x" << num << dec out << hex << "0x" << num << dec;
<< " (" << rawstr(const_string[num], 16) << ")"; break; out << " (" << rawstr(const_string[num], 16) << ")";
case op_addecp: case op_subecp: break;
case op_mulecp: case op_divecp: case op_addecp:
out << hex << "0x" << num << dec case op_subecp:
<< " (" << const_number[num] << ") sp-1"; break; case op_mulecp:
case op_divecp:
out << hex << "0x" << num << dec;
out << " (" << const_number[num] << ") sp-1";
break;
case op_lnkecp: case op_lnkecp:
out << hex << "0x" << num << dec out << hex << "0x" << num << dec;
<< " (" << rawstr(const_string[num], 16) << ") sp-1"; break; out << " (" << rawstr(const_string[num], 16) << ") sp-1";
case op_addc: case op_subc: break;
case op_mulc: case op_divc: case op_addc:
case op_lessc: case op_leqc: case op_subc:
case op_grtc: case op_geqc: case op_mulc:
case op_divc:
case op_lessc:
case op_leqc:
case op_grtc:
case op_geqc:
case op_pnum: case op_pnum:
out << hex << "0x" << num << dec out << hex << "0x" << num << dec;
<< " (" << const_number[num] << ")"; break; out << " (" << const_number[num] << ")";
case op_callvi: case op_newv: break;
case op_callfv: case op_repl: case op_callvi:
case op_intl: case op_findex: case op_newv:
case op_feach: case op_newf: case op_callfv:
case op_jmp: case op_jt: case op_repl:
case op_jf: case op_callg: case op_intl:
case op_mcallg: case op_loadg: case op_findex:
case op_calll: case op_mcalll: 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: case op_loadl:
out << hex << "0x" << num << dec; break; out << hex << "0x" << num << dec; break;
case op_callb: case op_callb:
out << hex << "0x" << num << " <" << natives[num].name out << hex << "0x" << num << " <" << natives[num].name
<< "@0x" << reinterpret_cast<u64>(natives[num].func) << "@0x" << reinterpret_cast<u64>(natives[num].func)
<< dec << ">"; break; << dec << ">"; break;
case op_upval: case op_mupval: case op_upval:
case op_mupval:
case op_loadu: case op_loadu:
out << hex << "0x" << ((num>>16)&0xffff) out << hex << "0x" << ((num>>16)&0xffff)
<< "[0x" << (num&0xffff) << "]" << dec; break; << "[0x" << (num&0xffff) << "]" << dec; break;
case op_happ: case op_pstr: case op_happ:
case op_lnkc: case op_callh: case op_pstr:
case op_mcallh: case op_para: case op_lnkc:
case op_deft: case op_dyn: case op_callh:
out << hex << "0x" << num << dec case op_mcallh:
<< " (" << rawstr(const_string[num], 16) << ")"; break; case op_para:
case op_deft:
case op_dyn:
out << hex << "0x" << num << dec;
out << " (" << rawstr(const_string[num], 16) << ")";
break;
default: default:
if (files) { if (files) {
out << hex << "0x" << num << dec; out << hex << "0x" << num << dec;
} }
break; break;
} }
// if file list is loaded, dump file location info
if (files) { if (files) {
out << " (" << files[code.fidx] << ":" << code.line << ")"; out << " (" << files[code.fidx] << ":" << code.line << ")";
} }

View File

@ -45,9 +45,9 @@ enum op_code_type: u8 {
op_muleq, // *= maybe pop stack top op_muleq, // *= maybe pop stack top
op_diveq, // /= maybe pop stack top op_diveq, // /= maybe pop stack top
op_lnkeq, // ~= maybe pop stack top op_lnkeq, // ~= maybe pop stack top
op_btandeq,// &= maybe pop stack top op_btandeq, // &= maybe pop stack top
op_btoreq, // |= maybe pop stack top op_btoreq, // |= maybe pop stack top
op_btxoreq,// ^= maybe pop stack top op_btxoreq, // ^= maybe pop stack top
op_addeqc, // += const don't pop stack top op_addeqc, // += const don't pop stack top
op_subeqc, // -= const don't pop stack top op_subeqc, // -= const don't pop stack top
op_muleqc, // *= const don't pop stack top op_muleqc, // *= const don't pop stack top
@ -71,8 +71,8 @@ enum op_code_type: u8 {
op_geqc, // >= const compare operator op_geqc, // >= const compare operator
op_pop, // pop a value out of stack top op_pop, // pop a value out of stack top
op_jmp, // jump absolute address with no condition 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_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_jf, // used in conditional/loop, jmp when condition is false and POP STACK
op_cnt, // add counter for forindex/foreach op_cnt, // add counter for forindex/foreach
op_findex, // index counter on the top of forindex_stack plus 1 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_feach, // index counter on the top of forindex_stack plus 1 and get the value in vector
@ -100,8 +100,8 @@ enum op_code_type: u8 {
struct opcode { struct opcode {
u8 op; // opcode u8 op; // opcode
u16 fidx; // source code file index u16 fidx; // source code file index
u32 num; // immediate num u64 num; // immediate num
u32 line; // location line of source code u64 line; // location line of source code
opcode() = default; opcode() = default;
opcode(const opcode&) = default; opcode(const opcode&) = default;
opcode& operator=(const opcode&) = default; opcode& operator=(const opcode&) = default;
@ -110,24 +110,23 @@ struct opcode {
class codestream { class codestream {
private: private:
opcode code; opcode code;
const u32 index; const u64 index;
inline static const f64* const_number = nullptr; inline static const f64* const_number = nullptr;
inline static const std::string* const_string = nullptr; inline static const std::string* const_string = nullptr;
inline static const nasal_builtin_table* natives = nullptr; inline static const nasal_builtin_table* natives = nullptr;
inline static const std::string* files = nullptr; inline static const std::string* files = nullptr;
public: public:
codestream(const opcode& c, const u32 i): code(c), index(i) {} codestream(const opcode& c, const u64 i): code(c), index(i) {}
static void set( static void set(const f64*,
const f64*, const std::string*, const std::string*,
const nasal_builtin_table*, const nasal_builtin_table*,
const std::string* file_list = nullptr const std::string* file_list = nullptr);
);
void dump(std::ostream&) const; void dump(std::ostream&) const;
}; };
std::ostream& operator<<(std::ostream&, const codestream&); std::ostream& operator<<(std::ostream&, const codestream&);
extern const char* opname[]; extern const char* oprand_name_table[];
} }

View File

@ -105,7 +105,7 @@ bool parse::check_comma(const tok* panic_set) {
} }
bool parse::check_tuple() { bool parse::check_tuple() {
u32 check_ptr=ptr, curve=1, bracket=0, brace=0; u64 check_ptr = ptr, curve = 1, bracket = 0, brace = 0;
while(toks[++check_ptr].type!=tok::eof && curve) { while(toks[++check_ptr].type!=tok::eof && curve) {
switch(toks[check_ptr].type) { switch(toks[check_ptr].type) {
case tok::lcurve: ++curve; break; case tok::lcurve: ++curve; break;
@ -156,8 +156,8 @@ bool parse::check_in_curve_multi_definition() {
} }
bool parse::check_special_call() { bool parse::check_special_call() {
// special call means like this: function_name(a:1,b:2,c:3); // special call means like this: function_name(a:1, b:2, c:3);
u32 check_ptr = ptr, curve = 1, bracket = 0, brace = 0; u64 check_ptr = ptr, curve = 1, bracket = 0, brace = 0;
while(toks[++check_ptr].type!=tok::eof && curve) { while(toks[++check_ptr].type!=tok::eof && curve) {
switch(toks[check_ptr].type) { switch(toks[check_ptr].type) {
case tok::lcurve: ++curve; break; case tok::lcurve: ++curve; break;
@ -254,11 +254,11 @@ vector_expr* parse::vec() {
// panic set for this token is not ',' // panic set for this token is not ','
// this is the FIRST set of calculation // this is the FIRST set of calculation
// array end with tok::null=0 // array end with tok::null=0
const tok panic[]={ const tok panic[] = {
tok::id,tok::str,tok::num,tok::tktrue, tok::id, tok::str, tok::num, tok::tktrue,
tok::tkfalse,tok::opnot,tok::sub,tok::tknil, tok::tkfalse, tok::opnot, tok::sub, tok::nil,
tok::func,tok::var,tok::lcurve,tok::floater, tok::func, tok::var, tok::lcurve, tok::floater,
tok::lbrace,tok::lbracket,tok::null tok::lbrace, tok::lbracket, tok::null
}; };
auto node = new vector_expr(toks[ptr].loc); auto node = new vector_expr(toks[ptr].loc);
match(tok::lbracket); match(tok::lbracket);
@ -372,7 +372,7 @@ expr* parse::expression() {
} }
switch(type) { switch(type) {
case tok::use: return use_stmt_gen(); case tok::use: return use_stmt_gen();
case tok::tknil: case tok::nil:
case tok::num: case tok::num:
case tok::str: case tok::str:
case tok::id: case tok::id:
@ -637,9 +637,9 @@ unary_operator* parse::unary() {
expr* parse::scalar() { expr* parse::scalar() {
expr* node = nullptr; expr* node = nullptr;
if (lookahead(tok::tknil)) { if (lookahead(tok::nil)) {
node = nil(); node = nil();
match(tok::tknil); match(tok::nil);
} else if (lookahead(tok::num)) { } else if (lookahead(tok::num)) {
node = num(); node = num();
} else if (lookahead(tok::str)) { } else if (lookahead(tok::str)) {
@ -713,11 +713,11 @@ call_vector* parse::callv() {
// panic set for this token is not ',' // panic set for this token is not ','
// this is the FIRST set of subvec // this is the FIRST set of subvec
// array end with tok::null=0 // array end with tok::null=0
const tok panic[]={ const tok panic[] = {
tok::id,tok::str,tok::num,tok::tktrue, tok::id, tok::str, tok::num, tok::tktrue,
tok::tkfalse,tok::opnot,tok::sub,tok::tknil, tok::tkfalse, tok::opnot, tok::sub, tok::nil,
tok::func,tok::var,tok::lcurve,tok::floater, tok::func, tok::var, tok::lcurve, tok::floater,
tok::lbrace,tok::lbracket,tok::colon,tok::null tok::lbrace, tok::lbracket, tok::colon, tok::null
}; };
auto node = new call_vector(toks[ptr].loc); auto node = new call_vector(toks[ptr].loc);
match(tok::lbracket); match(tok::lbracket);
@ -743,11 +743,11 @@ call_function* parse::callf() {
// panic set for this token is not ',' // panic set for this token is not ','
// this is the FIRST set of calculation/hashmember // this is the FIRST set of calculation/hashmember
// array end with tok::null=0 // array end with tok::null=0
const tok panic[]={ const tok panic[] = {
tok::id,tok::str,tok::num,tok::tktrue, tok::id, tok::str, tok::num, tok::tktrue,
tok::tkfalse,tok::opnot,tok::sub,tok::tknil, tok::tkfalse, tok::opnot, tok::sub, tok::nil,
tok::func,tok::var,tok::lcurve,tok::floater, tok::func, tok::var, tok::lcurve, tok::floater,
tok::lbrace,tok::lbracket,tok::null tok::lbrace, tok::lbracket, tok::null
}; };
auto node = new call_function(toks[ptr].loc); auto node = new call_function(toks[ptr].loc);
bool special_call=check_special_call(); bool special_call=check_special_call();
@ -802,7 +802,7 @@ expr* parse::definition() {
} }
multi_identifier* parse::incurve_def() { multi_identifier* parse::incurve_def() {
const auto& loc=toks[ptr].loc; const auto& loc = toks[ptr].loc;
match(tok::lcurve); match(tok::lcurve);
match(tok::var); match(tok::var);
auto node = multi_id(); auto node = multi_id();
@ -813,7 +813,7 @@ multi_identifier* parse::incurve_def() {
} }
multi_identifier* parse::outcurve_def() { multi_identifier* parse::outcurve_def() {
const auto& loc=toks[ptr].loc; const auto& loc = toks[ptr].loc;
match(tok::lcurve); match(tok::lcurve);
auto node = multi_id(); auto node = multi_id();
update_location(node); update_location(node);
@ -840,12 +840,13 @@ multi_identifier* parse::multi_id() {
} }
tuple_expr* parse::multi_scalar() { tuple_expr* parse::multi_scalar() {
// if check_call_memory is true,we will check if value called here can reach a memory space // if check_call_memory is true,
const tok panic[]={ // we will check if value called here can reach a memory space
tok::id,tok::str,tok::num,tok::tktrue, const tok panic[] = {
tok::tkfalse,tok::opnot,tok::sub,tok::tknil, tok::id, tok::str, tok::num, tok::tktrue,
tok::func,tok::var,tok::lcurve,tok::floater, tok::tkfalse, tok::opnot, tok::sub, tok::nil,
tok::lbrace,tok::lbracket,tok::null tok::func, tok::var, tok::lcurve, tok::floater,
tok::lbrace, tok::lbracket, tok::null
}; };
auto node = new tuple_expr(toks[ptr].loc); auto node = new tuple_expr(toks[ptr].loc);
match(tok::lcurve); match(tok::lcurve);
@ -1069,7 +1070,7 @@ return_expr* parse::return_expression() {
auto node = new return_expr(toks[ptr].loc); auto node = new return_expr(toks[ptr].loc);
match(tok::ret); match(tok::ret);
tok type = toks[ptr].type; tok type = toks[ptr].type;
if (type==tok::tknil || type==tok::num || if (type==tok::nil || type==tok::num ||
type==tok::str || type==tok::id || type==tok::str || type==tok::id ||
type==tok::func || type==tok::sub || type==tok::func || type==tok::sub ||
type==tok::opnot || type==tok::lcurve || type==tok::opnot || type==tok::lcurve ||

View File

@ -15,9 +15,9 @@ class parse {
#define prevspan (ptr!=0? toks[ptr-1].loc:toks[ptr].loc) #define prevspan (ptr!=0? toks[ptr-1].loc:toks[ptr].loc)
private: private:
u32 ptr; u64 ptr;
u32 in_func; // count function block u64 in_func; // count function block
u32 in_loop; // count loop block u64 in_loop; // count loop block
const token* toks; const token* toks;
code_block* root; code_block* root;
error err; error err;
@ -37,7 +37,7 @@ private:
{tok::rif ,"if" }, {tok::rif ,"if" },
{tok::elsif ,"elsif" }, {tok::elsif ,"elsif" },
{tok::relse ,"else" }, {tok::relse ,"else" },
{tok::tknil ,"nil" }, {tok::nil ,"nil" },
{tok::lcurve ,"(" }, {tok::lcurve ,"(" },
{tok::rcurve ,")" }, {tok::rcurve ,")" },
{tok::lbracket,"[" }, {tok::lbracket,"[" },

View File

@ -42,6 +42,7 @@ var nas_hash::get_value(const std::string& key) {
} else if (!elems.count("parents")) { } else if (!elems.count("parents")) {
return var::none(); return var::none();
} }
auto ret = var::none(); auto ret = var::none();
auto& val = elems.at("parents"); auto& val = elems.at("parents");
if (!val.is_vec()) { if (!val.is_vec()) {
@ -64,12 +65,14 @@ var* nas_hash::get_memory(const std::string& key) {
} else if (!elems.count("parents")) { } else if (!elems.count("parents")) {
return nullptr; return nullptr;
} }
var* addr = nullptr; var* addr = nullptr;
var& val = elems.at("parents"); var& val = elems.at("parents");
if (!val.is_vec()) { if (!val.is_vec()) {
return addr; return addr;
} }
for(auto& i : val.vec().elems) { for(auto& i : val.vec().elems) {
// recursively search key in `parents`
if (i.is_hash()) { if (i.is_hash()) {
addr = i.hash().get_memory(key); addr = i.hash().get_memory(key);
} }
@ -95,6 +98,30 @@ std::ostream& operator<<(std::ostream& out, nas_hash& hash) {
return out; return out;
} }
std::ostream& operator<<(std::ostream& out, nas_func& func) {
out << "func(";
std::vector<std::string> argument_list = {};
argument_list.resize(func.keys.size());
for(const auto& key : func.keys) {
argument_list[key.second-1] = key.first;
}
for(const auto& key : argument_list) {
out << key;
if (key != argument_list.back()) {
out << ", ";
}
}
if (func.dynamic_parameter_index>=0) {
out << (argument_list.size()? ", ":"");
out << func.dynamic_parameter_name << "...";
}
out << ") {..}";
return out;
}
void nas_func::clear() { void nas_func::clear() {
dynamic_parameter_index = -1; dynamic_parameter_index = -1;
local.clear(); local.clear();
@ -102,8 +129,7 @@ void nas_func::clear() {
keys.clear(); keys.clear();
} }
void nas_ghost::set( void nas_ghost::set(const std::string& ghost_type_name,
const std::string& ghost_type_name,
destructor destructor_pointer, destructor destructor_pointer,
marker gc_marker_pointer, marker gc_marker_pointer,
void* ghost_pointer) { void* ghost_pointer) {
@ -145,14 +171,14 @@ void nas_co::clear() {
if (!ctx.stack) { if (!ctx.stack) {
return; return;
} }
for(u32 i = 0; i<STACK_DEPTH; ++i) { for(u32 i = 0; i<VM_STACK_DEPTH; ++i) {
ctx.stack[i] = var::nil(); ctx.stack[i] = var::nil();
} }
ctx.pc = 0; ctx.pc = 0;
ctx.localr = nullptr; ctx.localr = nullptr;
ctx.memr = nullptr; ctx.memr = nullptr;
ctx.canary = ctx.stack+STACK_DEPTH-1; ctx.canary = ctx.stack+VM_STACK_DEPTH-1;
ctx.top = ctx.stack; ctx.top = ctx.stack;
ctx.funcr = var::nil(); ctx.funcr = var::nil();
ctx.upvalr = var::nil(); ctx.upvalr = var::nil();
@ -198,7 +224,7 @@ std::ostream& operator<<(std::ostream& out, nas_map& mp) {
nas_val::nas_val(vm_type val_type) { nas_val::nas_val(vm_type val_type) {
mark = gc_status::collected; mark = gc_status::collected;
type = val_type; type = val_type;
unmutable = 0; immutable = 0;
switch(val_type) { switch(val_type) {
case vm_type::vm_str: ptr.str = new std::string; break; case vm_type::vm_str: ptr.str = new std::string; break;
case vm_type::vm_vec: ptr.vec = new nas_vec; break; case vm_type::vm_vec: ptr.vec = new nas_vec; break;
@ -218,8 +244,8 @@ nas_val::~nas_val() {
case vm_type::vm_vec: delete ptr.vec; break; case vm_type::vm_vec: delete ptr.vec; break;
case vm_type::vm_hash: delete ptr.hash; break; case vm_type::vm_hash: delete ptr.hash; break;
case vm_type::vm_func: delete ptr.func; break; case vm_type::vm_func: delete ptr.func; break;
case vm_type::vm_upval:delete ptr.upval; break; case vm_type::vm_upval: delete ptr.upval; break;
case vm_type::vm_ghost:delete ptr.obj; break; case vm_type::vm_ghost: delete ptr.obj; break;
case vm_type::vm_co: delete ptr.co; break; case vm_type::vm_co: delete ptr.co; break;
case vm_type::vm_map: delete ptr.map; break; case vm_type::vm_map: delete ptr.map; break;
default: break; default: break;
@ -233,8 +259,8 @@ void nas_val::clear() {
case vm_type::vm_vec: ptr.vec->elems.clear(); break; case vm_type::vm_vec: ptr.vec->elems.clear(); break;
case vm_type::vm_hash: ptr.hash->elems.clear(); break; case vm_type::vm_hash: ptr.hash->elems.clear(); break;
case vm_type::vm_func: ptr.func->clear(); break; case vm_type::vm_func: ptr.func->clear(); break;
case vm_type::vm_upval:ptr.upval->clear(); break; case vm_type::vm_upval: ptr.upval->clear(); break;
case vm_type::vm_ghost:ptr.obj->clear(); break; case vm_type::vm_ghost: ptr.obj->clear(); break;
case vm_type::vm_co: ptr.co->clear(); break; case vm_type::vm_co: ptr.co->clear(); break;
case vm_type::vm_map: ptr.map->clear(); break; case vm_type::vm_map: ptr.map->clear(); break;
default: break; default: break;
@ -254,7 +280,10 @@ std::string var::to_str() {
tmp.erase(tmp.find_last_not_of('.')+1, std::string::npos); tmp.erase(tmp.find_last_not_of('.')+1, std::string::npos);
return tmp; return tmp;
} }
return "";
std::stringstream ss;
ss << *this;
return ss.str();
} }
std::ostream& operator<<(std::ostream& out, var& ref) { std::ostream& operator<<(std::ostream& out, var& ref) {
@ -265,8 +294,8 @@ std::ostream& operator<<(std::ostream& out, var& ref) {
case vm_type::vm_str: out << ref.str(); break; case vm_type::vm_str: out << ref.str(); break;
case vm_type::vm_vec: out << ref.vec(); break; case vm_type::vm_vec: out << ref.vec(); break;
case vm_type::vm_hash: out << ref.hash(); break; case vm_type::vm_hash: out << ref.hash(); break;
case vm_type::vm_func: out << "func(..) {..}"; break; case vm_type::vm_func: out << ref.func(); break;
case vm_type::vm_ghost:out << ref.ghost(); break; case vm_type::vm_ghost: out << ref.ghost(); break;
case vm_type::vm_co: out << ref.co(); break; case vm_type::vm_co: out << ref.co(); break;
case vm_type::vm_map: out << ref.map(); break; case vm_type::vm_map: out << ref.map(); break;
default: break; default: break;
@ -279,14 +308,14 @@ bool var::object_check(const std::string& name) {
} }
var var::none() { var var::none() {
return {vm_type::vm_none, static_cast<u32>(0)}; return {vm_type::vm_none, static_cast<u64>(0)};
} }
var var::nil() { var var::nil() {
return {vm_type::vm_nil, static_cast<u32>(0)}; return {vm_type::vm_nil, static_cast<u64>(0)};
} }
var var::ret(u32 pc) { var var::ret(u64 pc) {
return {vm_type::vm_ret, pc}; return {vm_type::vm_ret, pc};
} }
@ -310,7 +339,7 @@ var* var::addr() {
return val.addr; return val.addr;
} }
u32 var::ret() const { u64 var::ret() const {
return val.ret; return val.ret;
} }
@ -355,7 +384,7 @@ nas_map& var::map() {
} }
var nas_err(const std::string& error_function_name, const std::string& info) { var nas_err(const std::string& error_function_name, const std::string& info) {
std::cerr << "[vm] " << error_function_name << ": " << info << "\n"; std::cerr << "\n[vm] " << error_function_name << ": " << info << "\n";
return var::none(); return var::none();
} }

View File

@ -15,6 +15,7 @@ enum class vm_type: u8 {
vm_ret, // return addres(program counter) vm_ret, // return addres(program counter)
vm_nil, // nil vm_nil, // nil
vm_num, // number vm_num, // number
/* gc object */ /* gc object */
vm_str, // string vm_str, // string
vm_vec, // vector vm_vec, // vector
@ -24,6 +25,7 @@ enum class vm_type: u8 {
vm_ghost, // ghost type vm_ghost, // ghost type
vm_co, // coroutine vm_co, // coroutine
vm_map, // for globals and namespaces vm_map, // for globals and namespaces
/* mark type range */ /* mark type range */
vm_type_size_max vm_type_size_max
}; };
@ -49,7 +51,7 @@ struct var {
public: public:
vm_type type = vm_type::vm_none; vm_type type = vm_type::vm_none;
union { union {
u32 ret; u64 ret;
i64 cnt; i64 cnt;
f64 num; f64 num;
var* addr; var* addr;
@ -57,7 +59,7 @@ public:
} val; } val;
private: private:
var(vm_type t, u32 pc) {type = t; val.ret = pc;} var(vm_type t, u64 pc) {type = t; val.ret = pc;}
var(vm_type t, i64 ct) {type = t; val.cnt = ct;} var(vm_type t, i64 ct) {type = t; val.cnt = ct;}
var(vm_type t, f64 n) {type = t; val.num = n;} var(vm_type t, f64 n) {type = t; val.num = n;}
var(vm_type t, var* p) {type = t; val.addr = p;} var(vm_type t, var* p) {type = t; val.addr = p;}
@ -82,7 +84,7 @@ public:
// create new var object // create new var object
static var none(); static var none();
static var nil(); static var nil();
static var ret(u32); static var ret(u64);
static var cnt(i64); static var cnt(i64);
static var num(f64); static var num(f64);
static var gcobj(nas_val*); static var gcobj(nas_val*);
@ -91,7 +93,7 @@ public:
public: public:
// get value // get value
var* addr(); var* addr();
u32 ret() const; u64 ret() const;
i64& cnt(); i64& cnt();
f64 num() const; f64 num() const;
std::string& str(); std::string& str();
@ -143,19 +145,23 @@ struct nas_hash {
}; };
struct nas_func { struct nas_func {
i32 dynamic_parameter_index; // dynamic parameter name index in hash. i64 dynamic_parameter_index; // dynamic parameter name index in hash.
u32 entry; // pc will set to entry-1 to call this function u64 entry; // pc will set to entry-1 to call this function
u32 parameter_size; // used to load default parameters to a new function u32 parameter_size; // used to load default parameters to a new function
u32 local_size; // used to expand memory space for local values on stack u64 local_size; // used to expand memory space for local values on stack
std::vector<var> local; // local scope with default value(var) std::vector<var> local; // local scope with default value(var)
std::vector<var> upval; // closure std::vector<var> upval; // closure
// parameter table, u32 begins from 1 // parameter table, u32 begins from 1
std::unordered_map<std::string, u32> keys; std::unordered_map<std::string, u32> keys;
// dynamic parameter name
std::string dynamic_parameter_name;
nas_func(): nas_func():
dynamic_parameter_index(-1), entry(0), dynamic_parameter_index(-1), entry(0),
parameter_size(0), local_size(0) {} parameter_size(0), local_size(0),
dynamic_parameter_name("") {}
void clear(); void clear();
}; };
@ -163,7 +169,7 @@ struct nas_upval {
public: public:
/* on stack, use these variables */ /* on stack, use these variables */
bool on_stack; bool on_stack;
u32 size; u64 size;
var* stack_frame_offset; var* stack_frame_offset;
/* not on stack, use this */ /* not on stack, use this */
@ -207,7 +213,7 @@ public:
}; };
struct context { struct context {
u32 pc = 0; u64 pc = 0;
var* localr = nullptr; var* localr = nullptr;
var* memr = nullptr; var* memr = nullptr;
var funcr = var::nil(); var funcr = var::nil();
@ -228,7 +234,7 @@ struct nas_co {
status status; status status;
nas_co() { nas_co() {
ctx.stack = new var[STACK_DEPTH]; ctx.stack = new var[VM_STACK_DEPTH];
clear(); clear();
} }
~nas_co() { ~nas_co() {
@ -250,7 +256,7 @@ struct nas_map {
}; };
struct nas_val { struct nas_val {
enum class gc_status:u8 { enum class gc_status: u8 {
uncollected = 0, uncollected = 0,
collected, collected,
found found
@ -258,7 +264,7 @@ struct nas_val {
gc_status mark; gc_status mark;
vm_type type; // value type vm_type type; // value type
u8 unmutable; // used to mark if a string is unmutable u8 immutable; // used to mark if a string is immutable
union { union {
std::string* str; std::string* str;
nas_vec* vec; nas_vec* vec;
@ -277,6 +283,7 @@ struct nas_val {
std::ostream& operator<<(std::ostream&, nas_vec&); std::ostream& operator<<(std::ostream&, nas_vec&);
std::ostream& operator<<(std::ostream&, nas_hash&); std::ostream& operator<<(std::ostream&, nas_hash&);
std::ostream& operator<<(std::ostream&, nas_func&);
std::ostream& operator<<(std::ostream&, nas_map&); std::ostream& operator<<(std::ostream&, nas_map&);
std::ostream& operator<<(std::ostream&, const nas_ghost&); std::ostream& operator<<(std::ostream&, const nas_ghost&);
std::ostream& operator<<(std::ostream&, const nas_co&); std::ostream& operator<<(std::ostream&, const nas_co&);

View File

@ -2,15 +2,13 @@
namespace nasal { namespace nasal {
void vm::init( void vm::vm_init_enrty(const std::vector<std::string>& strs,
const std::vector<std::string>& strs,
const std::vector<f64>& nums, const std::vector<f64>& nums,
const std::vector<nasal_builtin_table>& natives, const std::vector<nasal_builtin_table>& natives,
const std::vector<opcode>& code, const std::vector<opcode>& code,
const std::unordered_map<std::string, i32>& global_symbol, const std::unordered_map<std::string, u64>& global_symbol,
const std::vector<std::string>& filenames, const std::vector<std::string>& filenames,
const std::vector<std::string>& argv const std::vector<std::string>& argv) {
) {
const_number = nums.data(); const_number = nums.data();
const_string = strs.data(); const_string = strs.data();
bytecode = code.data(); bytecode = code.data();
@ -51,14 +49,14 @@ void vm::context_and_global_init() {
ctx.funcr = nil; ctx.funcr = nil;
ctx.upvalr = nil; ctx.upvalr = nil;
/* set canary = stack[STACK_DEPTH-1] */ /* set canary = stack[VM_STACK_DEPTH-1] */
ctx.canary = ctx.stack+STACK_DEPTH-1; ctx.canary = ctx.stack+VM_STACK_DEPTH-1;
/* nothing is on stack */ /* nothing is on stack */
ctx.top = ctx.stack - 1; ctx.top = ctx.stack - 1;
/* clear main stack and global */ /* clear main stack and global */
for(u32 i = 0; i<STACK_DEPTH; ++i) { for(u32 i = 0; i<VM_STACK_DEPTH; ++i) {
ctx.stack[i] = nil; ctx.stack[i] = nil;
global[i] = nil; global[i] = nil;
} }
@ -80,8 +78,8 @@ void vm::value_info(var& val) {
<< "> " << rawstr(val.str(), 16) << "> " << rawstr(val.str(), 16)
<< std::dec; break; << std::dec; break;
case vm_type::vm_func: std::clog << "| func | <0x" << std::hex << p case vm_type::vm_func: std::clog << "| func | <0x" << std::hex << p
<< "> entry:0x" << val.func().entry << std::dec << "> " << val.func();
<< std::dec; break; break;
case vm_type::vm_upval:std::clog << "| upval| <0x" << std::hex << p case vm_type::vm_upval:std::clog << "| upval| <0x" << std::hex << p
<< std::dec << "> [" << val.upval().size << std::dec << "> [" << val.upval().size
<< " val]"; break; << " val]"; break;
@ -126,7 +124,7 @@ void vm::function_detail_info(const nas_func& func) {
} }
if (func.dynamic_parameter_index>=0) { if (func.dynamic_parameter_index>=0) {
std::clog << (argument_list.size()? ", ":""); std::clog << (argument_list.size()? ", ":"");
std::clog << const_string[func.dynamic_parameter_index] << "..."; std::clog << func.dynamic_parameter_name << "...";
} }
std::clog << ") "; std::clog << ") ";
const auto& code = bytecode[func.entry]; const auto& code = bytecode[func.entry];
@ -203,12 +201,15 @@ void vm::trace_back() {
// the first called place has no same calls // the first called place has no same calls
} }
void vm::stack_info(const u32 limit = 10) { void vm::stack_info(const u64 limit = 16) {
var* top = ctx.top; var* top = ctx.top;
var* bottom = ctx.stack; var* bottom = ctx.stack;
std::clog << "\nstack (0x" << std::hex << reinterpret_cast<u64>(bottom); const auto stack_address = reinterpret_cast<u64>(bottom);
std::clog << std::dec << ", limit " << limit << ", total ";
std::clog << "\nvm stack (0x" << std::hex << stack_address << std::dec;
std::clog << ", limit " << limit << ", total ";
std::clog << (top<bottom? 0:static_cast<i64>(top-bottom+1)) << ")\n"; std::clog << (top<bottom? 0:static_cast<i64>(top-bottom+1)) << ")\n";
for(u32 i = 0; i<limit && top>=bottom; ++i, --top) { for(u32 i = 0; i<limit && top>=bottom; ++i, --top) {
std::clog << " 0x" << std::hex std::clog << " 0x" << std::hex
<< std::setw(6) << std::setfill('0') << std::setw(6) << std::setfill('0')
@ -390,7 +391,7 @@ std::string vm::type_name_string(const var& value) const {
} }
void vm::die(const std::string& str) { void vm::die(const std::string& str) {
std::cerr << "[vm] error: " << str << "\n"; std::cerr << "\n[vm] error: " << str << "\n";
function_call_trace(); function_call_trace();
trace_back(); trace_back();
stack_info(); stack_info();
@ -411,11 +412,10 @@ void vm::die(const std::string& str) {
} }
} }
void vm::run( void vm::run(const codegen& gen,
const codegen& gen,
const linker& linker, const linker& linker,
const std::vector<std::string>& argv) { const std::vector<std::string>& argv) {
init( vm_init_enrty(
gen.strs(), gen.strs(),
gen.nums(), gen.nums(),
gen.natives(), gen.natives(),
@ -424,6 +424,7 @@ void vm::run(
linker.get_file_list(), linker.get_file_list(),
argv argv
); );
#ifndef _MSC_VER #ifndef _MSC_VER
// using labels as values/computed goto // using labels as values/computed goto
const void* oprs[] = { const void* oprs[] = {
@ -451,7 +452,7 @@ void vm::run(
&&mcallv, &&mcallh, &&ret &&mcallv, &&mcallh, &&ret
}; };
std::vector<const void*> code; std::vector<const void*> code;
for(auto& i : gen.codes()) { for(const auto& i : gen.codes()) {
code.push_back(oprs[i.op]); code.push_back(oprs[i.op]);
imm.push_back(i.num); imm.push_back(i.num);
} }
@ -506,7 +507,7 @@ void vm::run(
&vm::o_ret &vm::o_ret
}; };
std::vector<nafunc> code; std::vector<nafunc> code;
for(auto& i : gen.codes()) { for(const auto& i : gen.codes()) {
code.push_back(oprs[i.op]); code.push_back(oprs[i.op]);
imm.push_back(i.num); imm.push_back(i.num);
} }

View File

@ -20,13 +20,15 @@ namespace nasal {
class vm { class vm {
protected: protected:
/* registers of vm */ /* vm context */
context ctx; // running context context ctx;
/* constants */ /* constants */
const f64* const_number = nullptr; // constant numbers const f64* const_number = nullptr;
const std::string* const_string = nullptr; // constant symbols and strings const std::string* const_string = nullptr;
std::vector<u32> imm; // immediate number table std::vector<u64> imm; // immediate number table
/* nasal native functions */
std::vector<nasal_builtin_table> native_function; std::vector<nasal_builtin_table> native_function;
/* garbage collector */ /* garbage collector */
@ -49,15 +51,13 @@ protected:
bool flag_limited_mode = false; bool flag_limited_mode = false;
/* vm initializing function */ /* vm initializing function */
void init( void vm_init_enrty(const std::vector<std::string>&,
const std::vector<std::string>&,
const std::vector<f64>&, const std::vector<f64>&,
const std::vector<nasal_builtin_table>&, const std::vector<nasal_builtin_table>&,
const std::vector<opcode>&, const std::vector<opcode>&,
const std::unordered_map<std::string, i32>&, const std::unordered_map<std::string, u64>&,
const std::vector<std::string>&, const std::vector<std::string>&,
const std::vector<std::string>& const std::vector<std::string>&);
);
void context_and_global_init(); void context_and_global_init();
/* debug functions */ /* debug functions */
@ -66,7 +66,7 @@ protected:
void function_detail_info(const nas_func&); void function_detail_info(const nas_func&);
void function_call_trace(); void function_call_trace();
void trace_back(); void trace_back();
void stack_info(const u32); void stack_info(const u64);
void register_info(); void register_info();
void global_state(); void global_state();
void local_state(); void local_state();
@ -79,9 +79,11 @@ protected:
std::string type_name_string(const var&) const; std::string type_name_string(const var&) const;
void die(const std::string&); void die(const std::string&);
protected:
/* vm calculation functions*/ /* vm calculation functions*/
inline bool cond(var&); inline bool cond(var&);
protected:
/* vm operands */ /* vm operands */
inline void o_repl(); inline void o_repl();
inline void o_intl(); inline void o_intl();
@ -174,8 +176,8 @@ public:
/* constructor of vm instance */ /* constructor of vm instance */
vm() { vm() {
ctx.stack = new var[STACK_DEPTH]; ctx.stack = new var[VM_STACK_DEPTH];
global = new var[STACK_DEPTH]; global = new var[VM_STACK_DEPTH];
} }
~vm() { ~vm() {
delete[] ctx.stack; delete[] ctx.stack;
@ -183,11 +185,9 @@ public:
} }
/* execution entry */ /* execution entry */
void run( void run(const codegen&, // get generated code
const codegen&, // get generated code
const linker&, // get list of used files const linker&, // get list of used files
const std::vector<std::string>& // get arguments input by command line const std::vector<std::string>&); // get command line arguments
);
/* set detail report info flag */ /* set detail report info flag */
void set_detail_report_info(bool flag) {verbose = flag;} void set_detail_report_info(bool flag) {verbose = flag;}
@ -251,8 +251,9 @@ inline void vm::o_newv() {
auto& vec = newv.vec().elems; auto& vec = newv.vec().elems;
vec.resize(imm[ctx.pc]); vec.resize(imm[ctx.pc]);
// use top-=imm[pc]-1 here will cause error if imm[pc] is 0 // use top-=imm[pc]-1 here will cause error if imm[pc] is 0
ctx.top = ctx.top-imm[ctx.pc]+1; ctx.top = ctx.top - imm[ctx.pc] + 1;
for(u32 i = 0; i<imm[ctx.pc]; ++i) {
for(u64 i = 0; i<imm[ctx.pc]; ++i) {
vec[i] = ctx.top[i]; vec[i] = ctx.top[i];
} }
ctx.top[0] = newv; ctx.top[0] = newv;
@ -270,17 +271,22 @@ inline void vm::o_newf() {
/* this means you create a new function in local scope */ /* this means you create a new function in local scope */
if (ctx.localr) { if (ctx.localr) {
// copy upval scope list from upper level function
func.upval = ctx.funcr.func().upval; func.upval = ctx.funcr.func().upval;
// function created in the same local scope shares one closure
// so this size & stk setting has no problem // function created in the same local scope shares same closure
var upval = (ctx.upvalr.is_nil())? var upval = (ctx.upvalr.is_nil())?
ngc.alloc(vm_type::vm_upval): ngc.alloc(vm_type::vm_upval):
ctx.upvalr; ctx.upvalr;
// if no upval scope exists, now it's time to create one
if (ctx.upvalr.is_nil()) {
upval.upval().size = ctx.funcr.func().local_size; upval.upval().size = ctx.funcr.func().local_size;
upval.upval().stack_frame_offset = ctx.localr; upval.upval().stack_frame_offset = ctx.localr;
func.upval.push_back(upval);
ctx.upvalr = upval; ctx.upvalr = upval;
} }
func.upval.push_back(upval);
}
} }
inline void vm::o_happ() { inline void vm::o_happ() {
@ -305,6 +311,7 @@ inline void vm::o_deft() {
inline void vm::o_dyn() { inline void vm::o_dyn() {
ctx.top[0].func().dynamic_parameter_index = imm[ctx.pc]; ctx.top[0].func().dynamic_parameter_index = imm[ctx.pc];
ctx.top[0].func().dynamic_parameter_name = const_string[imm[ctx.pc]];
} }
inline void vm::o_lnot() { inline void vm::o_lnot() {
@ -669,7 +676,7 @@ inline void vm::o_callvi() {
die("must use a vector but get "+type_name_string(val)); die("must use a vector but get "+type_name_string(val));
return; return;
} }
// cannot use operator[],because this may cause overflow // cannot use operator[], because this may cause overflow
(++ctx.top)[0] = val.vec().get_value(imm[ctx.pc]); (++ctx.top)[0] = val.vec().get_value(imm[ctx.pc]);
if (ctx.top[0].is_none()) { if (ctx.top[0].is_none()) {
die(report_out_of_range(imm[ctx.pc], val.vec().size())); die(report_out_of_range(imm[ctx.pc], val.vec().size()));
@ -683,6 +690,7 @@ inline void vm::o_callh() {
die("must call a hash but get "+type_name_string(val)); die("must call a hash but get "+type_name_string(val));
return; return;
} }
const auto& str = const_string[imm[ctx.pc]]; const auto& str = const_string[imm[ctx.pc]];
if (val.is_hash()) { if (val.is_hash()) {
ctx.top[0] = val.hash().get_value(str); ctx.top[0] = val.hash().get_value(str);
@ -700,7 +708,7 @@ inline void vm::o_callh() {
} }
inline void vm::o_callfv() { inline void vm::o_callfv() {
const u32 argc = imm[ctx.pc]; // arguments counter const auto argc = imm[ctx.pc]; // arguments counter
var* local = ctx.top-argc+1; // arguments begin address var* local = ctx.top-argc+1; // arguments begin address
if (!local[-1].is_func()) { if (!local[-1].is_func()) {
die("must call a function but get "+type_name_string(local[-1])); die("must call a function but get "+type_name_string(local[-1]));
@ -719,7 +727,7 @@ inline void vm::o_callfv() {
return; return;
} }
// parameter size is func->psize-1, 1 is reserved for "me" // parameter size is func->psize-1, 1 is reserved for "me"
const u32 parameter_size = func.parameter_size-1; const u64 parameter_size = func.parameter_size-1;
if (argc<parameter_size && func.local[argc+1].is_none()) { if (argc<parameter_size && func.local[argc+1].is_none()) {
die(report_lack_arguments(argc, func)); die(report_lack_arguments(argc, func));
return; return;
@ -730,16 +738,17 @@ inline void vm::o_callfv() {
if (func.dynamic_parameter_index>=0) { if (func.dynamic_parameter_index>=0) {
// load dynamic argument // load dynamic argument
dynamic = ngc.alloc(vm_type::vm_vec); dynamic = ngc.alloc(vm_type::vm_vec);
for(u32 i = parameter_size; i<argc; ++i) { for(u64 i = parameter_size; i<argc; ++i) {
dynamic.vec().elems.push_back(local[i]); dynamic.vec().elems.push_back(local[i]);
} }
} else if (parameter_size<argc) { } else if (parameter_size<argc) {
// load arguments to default dynamic argument "arg", located at stack+1 // load arguments to default dynamic argument "arg", located at stack+1
dynamic = ngc.alloc(vm_type::vm_vec); dynamic = ngc.alloc(vm_type::vm_vec);
for(u32 i = parameter_size; i<argc; ++i) { for(u64 i = parameter_size; i<argc; ++i) {
dynamic.vec().elems.push_back(local[i]); dynamic.vec().elems.push_back(local[i]);
} }
} }
// should reset stack top after allocating vector // should reset stack top after allocating vector
// because this may cause gc // because this may cause gc
// then all the available values the vector needs // then all the available values the vector needs
@ -747,14 +756,18 @@ inline void vm::o_callfv() {
// collected incorrectly // collected incorrectly
ctx.top = local+func.local_size; ctx.top = local+func.local_size;
const u32 min_size = (std::min)(parameter_size, argc); // avoid error in MSVC // use (std::min) to avoid compilation error in MSVC
for(u32 i = min_size; i>=1; --i) { // load arguments // MSVC windows.h uses macro std::min
const u64 min_size = (std::min)(parameter_size, argc);
// load arguments
for(u64 i = min_size; i>=1; --i) {
local[i] = local[i-1]; local[i] = local[i-1];
} }
local[0] = func.local[0];// load "me" local[0] = func.local[0]; // load "me"
// load local scope & default arguments // load local scope & default arguments
for(u32 i = min_size+1; i<func.local_size; ++i) { for(u64 i = min_size+1; i<func.local_size; ++i) {
local[i] = func.local[i]; local[i] = func.local[i];
} }
// load dynamic argument // load dynamic argument
@ -967,25 +980,31 @@ inline void vm::o_mcallv() {
} }
inline void vm::o_mcallh() { inline void vm::o_mcallh() {
var hash = ctx.top[0]; // mcall hash, reserved on stack to avoid gc // mcall hash, reserved on stack to avoid gc, so do not do ctx.top--
var hash = ctx.top[0];
if (!hash.is_hash() && !hash.is_map()) { if (!hash.is_hash() && !hash.is_map()) {
die("must call a hash/namespace but get "+type_name_string(hash)); die("must call a hash/namespace but get " + type_name_string(hash));
return; return;
} }
const auto& str = const_string[imm[ctx.pc]];
const auto& key = const_string[imm[ctx.pc]];
// map is for nasal namespace type, for example `globals`
if (hash.is_map()) { if (hash.is_map()) {
ctx.memr = hash.map().get_memory(str); ctx.memr = hash.map().get_memory(key);
if (!ctx.memr) { if (!ctx.memr) {
die("cannot find symbol \"" + str + "\""); die("cannot find symbol \"" + key + "\"");
} }
return; return;
} }
// call hash member
auto& ref = hash.hash(); auto& ref = hash.hash();
ctx.memr = ref.get_memory(str); ctx.memr = ref.get_memory(key);
// create a new key // create a new key if not exists
if (!ctx.memr) { if (!ctx.memr) {
ref.elems[str] = nil; ref.elems[key] = nil;
ctx.memr = ref.get_memory(str); ctx.memr = ref.get_memory(key);
} }
} }
@ -1023,7 +1042,7 @@ inline void vm::o_ret() {
auto size = func.func().local_size; auto size = func.func().local_size;
upval.on_stack = false; upval.on_stack = false;
upval.elems.resize(size); upval.elems.resize(size);
for(u32 i = 0; i<size; ++i) { for(u64 i = 0; i<size; ++i) {
upval.elems[i] = local[i]; upval.elems[i] = local[i];
} }
} }

View File

@ -41,14 +41,14 @@ var builtin_u32not(context* ctx, gc* ngc) {
} }
var builtin_fld(context* ctx, gc* ngc) { var builtin_fld(context* ctx, gc* ngc) {
// bits.fld(s,0,3); // bits.fld(s, 0, 3);
// if s stores 10100010(162) // if s stores 10100010(162)
// will get 101(5) // will get 101(5)
auto local = ctx->localr; auto local = ctx->localr;
auto str = local[1]; auto str = local[1];
auto startbit = local[2]; auto startbit = local[2];
auto length = local[3]; auto length = local[3];
if (!str.is_str() || str.val.gcobj->unmutable) { if (!str.is_str() || str.val.gcobj->immutable) {
return nas_err("bits::fld", "\"str\" must be mutable string"); return nas_err("bits::fld", "\"str\" must be mutable string");
} }
if (!startbit.is_num() || !length.is_num()) { if (!startbit.is_num() || !length.is_num()) {
@ -70,7 +70,7 @@ var builtin_fld(context* ctx, gc* ngc) {
} }
var builtin_sfld(context* ctx, gc* ngc) { var builtin_sfld(context* ctx, gc* ngc) {
// bits.sfld(s,0,3); // bits.sfld(s, 0, 3);
// if s stores 10100010(162) // if s stores 10100010(162)
// will get 101(5) then this will be signed extended to // will get 101(5) then this will be signed extended to
// 11111101(-3) // 11111101(-3)
@ -78,7 +78,7 @@ var builtin_sfld(context* ctx, gc* ngc) {
auto str = local[1]; auto str = local[1];
auto startbit = local[2]; auto startbit = local[2];
auto length = local[3]; auto length = local[3];
if (!str.is_str() || str.val.gcobj->unmutable) { if (!str.is_str() || str.val.gcobj->immutable) {
return nas_err("bits::sfld", "\"str\" must be mutable string"); return nas_err("bits::sfld", "\"str\" must be mutable string");
} }
if (!startbit.is_num() || !length.is_num()) { if (!startbit.is_num() || !length.is_num()) {
@ -103,7 +103,7 @@ var builtin_sfld(context* ctx, gc* ngc) {
} }
var builtin_setfld(context* ctx, gc* ngc) { var builtin_setfld(context* ctx, gc* ngc) {
// bits.setfld(s,0,8,69); // bits.setfld(s, 0, 8, 69);
// set 01000101(69) to string will get this: // set 01000101(69) to string will get this:
// 10100010(162) // 10100010(162)
// so s[0]=162 // so s[0]=162
@ -112,7 +112,7 @@ var builtin_setfld(context* ctx, gc* ngc) {
auto startbit = local[2]; auto startbit = local[2];
auto length = local[3]; auto length = local[3];
auto value = local[4]; auto value = local[4];
if (!str.is_str() || str.val.gcobj->unmutable) { if (!str.is_str() || str.val.gcobj->immutable) {
return nas_err("bits::setfld", "\"str\" must be mutable string"); return nas_err("bits::setfld", "\"str\" must be mutable string");
} }
if (!startbit.is_num() || !length.is_num() || !value.is_num()) { if (!startbit.is_num() || !length.is_num() || !value.is_num()) {

View File

@ -84,7 +84,7 @@ var builtin_read(context* ctx, gc* ngc) {
if (!file_descriptor.object_check(file_type_name)) { if (!file_descriptor.object_check(file_type_name)) {
return nas_err("io::read", "not a valid filehandle"); return nas_err("io::read", "not a valid filehandle");
} }
if (!buffer.is_str() || buffer.val.gcobj->unmutable) { if (!buffer.is_str() || buffer.val.gcobj->immutable) {
return nas_err("io::read", "\"buf\" must be mutable string"); return nas_err("io::read", "\"buf\" must be mutable string");
} }
if (!length.is_num()) { if (!length.is_num()) {
@ -102,7 +102,7 @@ var builtin_read(context* ctx, gc* ngc) {
static_cast<FILE*>(file_descriptor.ghost().pointer) static_cast<FILE*>(file_descriptor.ghost().pointer)
); );
buffer.str() = temp_buffer; buffer.str() = temp_buffer;
buffer.val.gcobj->unmutable = true; buffer.val.gcobj->immutable = true;
delete []temp_buffer; delete []temp_buffer;
return var::num(read_size); return var::num(read_size);
} }

View File

@ -204,16 +204,17 @@ var builtin_str(context* ctx, gc* ngc) {
var builtin_size(context* ctx, gc* ngc) { var builtin_size(context* ctx, gc* ngc) {
auto val = ctx->localr[1]; auto val = ctx->localr[1];
f64 num = 0;
usize num = 0;
switch(val.type) { switch(val.type) {
case vm_type::vm_num: num = val.num(); break; case vm_type::vm_num: return val;
case vm_type::vm_str: num = val.str().length(); break; case vm_type::vm_str: num = val.str().length(); break;
case vm_type::vm_vec: num = val.vec().size(); break; case vm_type::vm_vec: num = val.vec().size(); break;
case vm_type::vm_hash: num = val.hash().size(); break; case vm_type::vm_hash: num = val.hash().size(); break;
case vm_type::vm_map: num = val.map().mapper.size(); break; case vm_type::vm_map: num = val.map().mapper.size(); break;
default: break; default: break;
} }
return var::num(num); return var::num(static_cast<f64>(num));
} }
var builtin_time(context* ctx, gc* ngc) { var builtin_time(context* ctx, gc* ngc) {
@ -323,7 +324,7 @@ var builtin_substr(context* ctx, gc* ngc) {
if (begin>=str.str().length()) { if (begin>=str.str().length()) {
return nas_err("susbtr", "begin index out of range: "+std::to_string(begin)); return nas_err("susbtr", "begin index out of range: "+std::to_string(begin));
} }
return ngc->newstr(str.str().substr(begin,length)); return ngc->newstr(str.str().substr(begin, length));
} }
var builtin_streq(context* ctx, gc* ngc) { var builtin_streq(context* ctx, gc* ngc) {
@ -339,6 +340,7 @@ var builtin_left(context* ctx, gc* ngc) {
auto local = ctx->localr; auto local = ctx->localr;
var str = local[1]; var str = local[1];
var len = local[2]; var len = local[2];
if (!str.is_str()) { if (!str.is_str()) {
return nas_err("left", "\"string\" must be string"); return nas_err("left", "\"string\" must be string");
} }
@ -355,20 +357,23 @@ var builtin_right(context* ctx, gc* ngc) {
auto local = ctx->localr; auto local = ctx->localr;
var str = local[1]; var str = local[1];
var len = local[2]; var len = local[2];
if (!str.is_str()) { if (!str.is_str()) {
return nas_err("right", "\"string\" must be string"); return nas_err("right", "\"string\" must be string");
} }
if (!len.is_num()) { if (!len.is_num()) {
return nas_err("right", "\"length\" must be number"); return nas_err("right", "\"length\" must be number");
} }
i32 length = static_cast<i32>(len.num()); i32 length = static_cast<i32>(len.num());
i32 srclen = str.str().length(); i32 srclen = static_cast<i32>(str.str().length());
if (length>srclen) { if (length>srclen) {
length = srclen; length = srclen;
} }
if (length<0) { if (length<0) {
length = 0; length = 0;
} }
return ngc->newstr(str.str().substr(srclen-length, srclen)); return ngc->newstr(str.str().substr(srclen-length, srclen));
} }
@ -387,22 +392,22 @@ var builtin_cmp(context* ctx, gc* ngc) {
var builtin_chr(context* ctx, gc* ngc) { var builtin_chr(context* ctx, gc* ngc) {
const char* extend[] = { const char* extend[] = {
""," ","","ƒ","","","","", "", " ", "", "ƒ", "", "", "", "",
"ˆ","","Š","","Œ"," ","Ž"," ", "ˆ", "", "Š", "", "Œ", " ", "Ž", " ",
" ","","","","","","","", " ", "", "", "", "", "", "", "",
"˜","","š","","œ"," ","ž","Ÿ", "˜", "", "š", "", "œ", " ", "ž", "Ÿ",
" ","¡","¢","£","¤","¥","¦","§", " ", "¡", "¢", "£", "¤", "¥", "¦", "§",
"¨","©","ª","«","¬"," ","®","¯", "¨", "©", "ª", "«", "¬", " ", "®", "¯",
"°","±","²","³","´","µ","","·", "°", "±", "²", "³", "´", "µ", "", "·",
"¸","¹","º","»","¼","½","¾","¿", "¸", "¹", "º", "»", "¼", "½", "¾", "¿",
"À","Á","Â","Ã","Ä","Å","Æ","Ç", "À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç",
"È","É","Ê","Ë","Ì","Í","Î","Ï", "È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï",
"Ð","Ñ","Ò","Ó","Ô","Õ","Ö","×", "Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö", "×",
"Ø","Ù","Ú","Û","Ü","Ý","Þ","ß", "Ø", "Ù", "Ú", "Û", "Ü", "Ý", "Þ", "ß",
"à","á","â","ã","ä","å","æ","ç", "à", "á", "â", "ã", "ä", "å", "æ", "ç",
"è","é","ê","ë","ì","í","î","ï", "è", "é", "ê", "ë", "ì", "í", "î", "ï",
"ð","ñ","ò","ó","ô","õ","ö","÷", "ð", "ñ", "ò", "ó", "ô", "õ", "ö", "÷",
"ø","ù","ú","û","ü","ý","þ","ÿ" "ø", "ù", "ú", "û", "ü", "ý", "þ", "ÿ"
}; };
auto num = static_cast<i32>(ctx->localr[1].num()); auto num = static_cast<i32>(ctx->localr[1].num());
if (0<=num && num<128) { if (0<=num && num<128) {
@ -479,7 +484,7 @@ std::string md5(const std::string& src) {
std::vector<u32> buff; std::vector<u32> buff;
usize num = ((src.length()+8)>>6)+1; usize num = ((src.length()+8)>>6)+1;
usize buffsize = num<<4; usize buffsize = num<<4;
buff.resize(buffsize,0); buff.resize(buffsize, 0);
for(usize i = 0; i<src.length(); i++) { for(usize i = 0; i<src.length(); i++) {
buff[i>>2] |= (static_cast<u8>(src[i]))<<((i&0x3)<<3); buff[i>>2] |= (static_cast<u8>(src[i]))<<((i&0x3)<<3);
} }
@ -489,37 +494,45 @@ std::string md5(const std::string& src) {
// u32(abs(sin(i+1))*(2pow32)) // u32(abs(sin(i+1))*(2pow32))
const u32 k[] = { const u32 k[] = {
0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501, 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,0xa679438e,0x49b40821, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
}; };
// left shift bits // left shift bits
const u32 s[] = { const u32 s[] = {
7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
}; };
// index // index
const u32 idx[] = { const u32 idx[] = {
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, // g=i 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // g=i
1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12, // g=(5*i+1)%16; 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, // g=(5*i+1)%16;
5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2, // g=(3*i+5)%16; 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, // g=(3*i+5)%16;
0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9 // g=(7*i)%16; 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 // g=(7*i)%16;
}; };
#define shift(x,n) (((x)<<(n))|((x)>>(32-(n)))) // cycle left shift #define shift(x, n) (((x)<<(n))|((x)>>(32-(n)))) // cycle left shift
#define md5f(x,y,z) (((x)&(y))|((~x)&(z))) #define md5f(x, y, z) (((x)&(y))|((~x)&(z)))
#define md5g(x,y,z) (((x)&(z))|((y)&(~z))) #define md5g(x, y, z) (((x)&(z))|((y)&(~z)))
#define md5h(x,y,z) ((x)^(y)^(z)) #define md5h(x, y, z) ((x)^(y)^(z))
#define md5i(x,y,z) ((y)^((x)|(~z))) #define md5i(x, y, z) ((y)^((x)|(~z)))
u32 atmp = 0x67452301, btmp = 0xefcdab89; u32 atmp = 0x67452301, btmp = 0xefcdab89;
u32 ctmp = 0x98badcfe, dtmp = 0x10325476; u32 ctmp = 0x98badcfe, dtmp = 0x10325476;
@ -533,7 +546,7 @@ std::string md5(const std::string& src) {
u32 tmp = d; u32 tmp = d;
d = c; d = c;
c = b; c = b;
b = b+shift((a+f+k[j]+buff[i+idx[j]]),s[j]); b = b+shift((a+f+k[j]+buff[i+idx[j]]), s[j]);
a = tmp; a = tmp;
} }
atmp += a; atmp += a;
@ -565,13 +578,13 @@ public:
stamp = std::chrono::high_resolution_clock::now(); stamp = std::chrono::high_resolution_clock::now();
} }
f64 elapsed_milliseconds() { auto elapsed_milliseconds() const {
auto duration = std::chrono::high_resolution_clock::now() - stamp; const auto duration = std::chrono::high_resolution_clock::now() - stamp;
return std::chrono::duration_cast<std::chrono::milliseconds>(duration).count(); return std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
} }
f64 elapsed_microseconds() { auto elapsed_microseconds() const {
auto duration = std::chrono::high_resolution_clock::now() - stamp; const auto duration = std::chrono::high_resolution_clock::now() - stamp;
return std::chrono::duration_cast<std::chrono::microseconds>(duration).count(); return std::chrono::duration_cast<std::chrono::microseconds>(duration).count();
} }
}; };
@ -607,7 +620,7 @@ var builtin_elapsed_millisecond(context* ctx, gc* ngc) {
return var::num(-1); return var::num(-1);
} }
auto stamp = static_cast<time_stamp*>(object.ghost().pointer); auto stamp = static_cast<time_stamp*>(object.ghost().pointer);
return var::num(stamp->elapsed_milliseconds()); return var::num(static_cast<f64>(stamp->elapsed_milliseconds()));
} }
var builtin_elapsed_microsecond(context* ctx, gc* ngc) { var builtin_elapsed_microsecond(context* ctx, gc* ngc) {
@ -616,7 +629,7 @@ var builtin_elapsed_microsecond(context* ctx, gc* ngc) {
return var::num(-1); return var::num(-1);
} }
auto stamp = static_cast<time_stamp*>(object.ghost().pointer); auto stamp = static_cast<time_stamp*>(object.ghost().pointer);
return var::num(stamp->elapsed_microseconds()); return var::num(static_cast<f64>(stamp->elapsed_microseconds()));
} }
var builtin_gcextend(context* ctx, gc* ngc) { var builtin_gcextend(context* ctx, gc* ngc) {
@ -644,20 +657,27 @@ var builtin_gcextend(context* ctx, gc* ngc) {
} }
var builtin_gcinfo(context* ctx, gc* ngc) { var builtin_gcinfo(context* ctx, gc* ngc) {
auto den = std::chrono::high_resolution_clock::duration::period::den; const auto den = std::chrono::high_resolution_clock::duration::period::den;
var res = ngc->alloc(vm_type::vm_hash); var res = ngc->alloc(vm_type::vm_hash);
double total = 0; f64 total = 0;
for(u32 i = 0; i<gc_type_size; ++i) { for(u32 i = 0; i<gc_type_size; ++i) {
total += ngc->gcnt[i]; total += static_cast<f64>(ngc->gcnt[i]);
} }
// using ms
auto& map = res.hash().elems; auto& map = res.hash().elems;
map["total"] = var::num(ngc->worktime*1.0/den*1000); const auto worktime = static_cast<f64>(ngc->worktime);
map["average"] = var::num(ngc->worktime*1.0/den*1000/total); const auto max_time = static_cast<f64>(ngc->max_time);
map["max_gc"] = var::num(ngc->max_time*1.0/den*1000); const auto max_mark_time = static_cast<f64>(ngc->max_mark_time);
map["max_mark"] = var::num(ngc->max_mark_time*1.0/den*1000); const auto max_sweep_time = static_cast<f64>(ngc->max_sweep_time);
map["max_sweep"] = var::num(ngc->max_sweep_time*1.0/den*1000);
// using ms
map["total"] = var::num(worktime/den*1000);
map["average"] = var::num(worktime/den*1000/total);
map["max_gc"] = var::num(max_time/den*1000);
map["max_mark"] = var::num(max_mark_time/den*1000);
map["max_sweep"] = var::num(max_sweep_time/den*1000);
return res; return res;
} }
@ -682,9 +702,17 @@ var builtin_ghosttype(context* ctx, gc* ngc) {
if (!arg.is_ghost()) { if (!arg.is_ghost()) {
return nas_err("ghosttype", "this is not a ghost object."); return nas_err("ghosttype", "this is not a ghost object.");
} }
const auto& name = arg.ghost().get_ghost_name(); const auto& name = arg.ghost().get_ghost_name();
// https://wiki.flightgear.org/Nasal_library#ghosttype()
// tolds us if no name has been set,
// return a unique id (the pointer to the instance)
if (!name.length()) { if (!name.length()) {
return var::num(reinterpret_cast<u64>(arg.ghost().pointer)); std::stringstream ss;
ss << "0x" << std::hex;
ss << reinterpret_cast<u64>(arg.ghost().pointer) << std::dec;
return ngc->newstr(ss.str());
} }
return ngc->newstr(name); return ngc->newstr(name);
} }

View File

@ -66,10 +66,12 @@ var builtin_values(context*, gc*);
var builtin_sleep(context*, gc*); var builtin_sleep(context*, gc*);
var builtin_platform(context*, gc*); var builtin_platform(context*, gc*);
var builtin_arch(context*, gc*); var builtin_arch(context*, gc*);
// md5 related functions // md5 related functions
std::string tohex(u32); std::string tohex(u32);
std::string md5(const std::string&); std::string md5(const std::string&);
var builtin_md5(context*, gc*); var builtin_md5(context*, gc*);
var builtin_maketimestamp(context*, gc*); var builtin_maketimestamp(context*, gc*);
var builtin_time_stamp(context*, gc*); var builtin_time_stamp(context*, gc*);
var builtin_elapsed_millisecond(context*, gc*); var builtin_elapsed_millisecond(context*, gc*);
@ -80,7 +82,7 @@ var builtin_logtime(context*, gc*);
var builtin_ghosttype(context*, gc*); var builtin_ghosttype(context*, gc*);
// register builtin function's name and it's address here in this table below // register builtin function's name and it's address here in this table below
// this table must end with {nullptr,nullptr} // this table must end with {nullptr, nullptr}
struct nasal_builtin_table { struct nasal_builtin_table {
const char* name; const char* name;
var (*func)(context*, gc*); var (*func)(context*, gc*);

View File

@ -37,7 +37,7 @@ void repl::update_temp_file() {
bool repl::check_need_more_input() { bool repl::check_need_more_input() {
while(true) { while(true) {
update_temp_file(); update_temp_file();
auto nasal_lexer = std::unique_ptr<lexer>(new lexer); auto nasal_lexer = std::make_unique<lexer>();
if (nasal_lexer->scan("<nasal-repl>").geterr()) { if (nasal_lexer->scan("<nasal-repl>").geterr()) {
return false; return false;
} }
@ -77,11 +77,11 @@ void repl::help() {
} }
bool repl::run() { bool repl::run() {
auto nasal_lexer = std::unique_ptr<lexer>(new lexer); auto nasal_lexer = std::make_unique<lexer>();
auto nasal_parser = std::unique_ptr<parse>(new parse); auto nasal_parser = std::make_unique<parse>();
auto nasal_linker = std::unique_ptr<linker>(new linker); auto nasal_linker = std::make_unique<linker>();
auto nasal_opt = std::unique_ptr<optimizer>(new optimizer); auto nasal_opt = std::make_unique<optimizer>();
auto nasal_codegen = std::unique_ptr<codegen>(new codegen); auto nasal_codegen = std::make_unique<codegen>();
update_temp_file(); update_temp_file();
if (nasal_lexer->scan("<nasal-repl>").geterr()) { if (nasal_lexer->scan("<nasal-repl>").geterr()) {

View File

@ -1,12 +1,13 @@
var student = func(n,a) { var student = func(n, a) {
return { return {
print_info:func println(n,' ',a), print_info: func println(n,' ',a),
set_age: func(age) a=age, set_age: func(age) a=age,
get_age: func return a, get_age: func return a,
set_name: func(name) n=name, set_name: func(name) n=name,
get_name: func return n get_name: func return n
}; };
} }
var s=student('valk',24); var s=student('valk',24);
s.print_info(); s.print_info();
println(s.get_age(),' ',s.get_name()); println(s.get_age(),' ',s.get_name());
@ -18,6 +19,7 @@ s.set_age(20);
s.set_name('Sidi Liang'); s.set_name('Sidi Liang');
s.print_info(); s.print_info();
println(s.get_age(),' ',s.get_name()); println(s.get_age(),' ',s.get_name());
# flightgear nasal-console cannot use this kind of object initializing # flightgear nasal-console cannot use this kind of object initializing
var m = func() { var m = func() {
var (_1,_2)=(0,1); var (_1,_2)=(0,1);

View File

@ -17,7 +17,7 @@ var test_func = func(test_processes...) {
info = runtime.gc.info(); info = runtime.gc.info();
println("[", os.time(), "] ", duration, " ms, gc ", println("[", os.time(), "] ", duration, " ms, gc ",
(info.total-gc_total)*100/duration, "%, ", (info.total-gc_total)*100/duration, "%, ",
int(1000/duration), " cps"); 1000/duration, " cps");
gc_total = info.total; gc_total = info.total;
} }

View File

@ -251,3 +251,19 @@ var test_single_id_iterator = 0;
foreach(test_single_id_iterator; [0, 1, 2, 3]) { foreach(test_single_id_iterator; [0, 1, 2, 3]) {
println(test_single_id_iterator); println(test_single_id_iterator);
} }
# simple closure test, make sure two functions share the same closure
var closure_tester = func() {
var b = 0;
return [
func { b += 1; },
func { return b; }
];
}();
for(var i = 1; i<=10; i += 1) {
closure_tester[0]();
if (closure_tester[1]()!=i) {
die("test failed: expect " ~ i ~ ", but get " ~ closure_tester[1]());
}
}

View File

@ -4,9 +4,6 @@ import os
import platform import platform
import shutil import shutil
nasal_version = "11.1"
build_directory = pathlib.Path("build") build_directory = pathlib.Path("build")
if not os.path.exists(build_directory): if not os.path.exists(build_directory):
print("pack binaries failed: build directory not found") print("pack binaries failed: build directory not found")
@ -47,20 +44,25 @@ for m in ["libfib", "libkey", "libmat", "libnasock"]:
tar_file_name = "nasal-{}".format(platform.system()) tar_file_name = "nasal-{}".format(platform.system())
# create package directory in build directory and copy files needed # create package directory in build directory and copy files needed
package_directory = build_directory.joinpath(tar_file_name) package_directory = build_directory.joinpath(tar_file_name)
if not os.path.exists(package_directory): if not os.path.exists(package_directory):
os.mkdir(package_directory) os.mkdir(package_directory)
os.mkdir(package_directory.joinpath("module")) os.mkdir(package_directory.joinpath("module"))
print("pack nasal executable") print("pack nasal executable")
shutil.copy(nasal_executable, package_directory.joinpath(nasal_executable)) shutil.copy(nasal_executable, package_directory.joinpath(nasal_executable))
print("pack nasal standard library") print("pack nasal standard library")
shutil.copytree(nasal_standard_library, package_directory.joinpath(nasal_standard_library)) shutil.copytree(nasal_standard_library, package_directory.joinpath(nasal_standard_library))
for m in nasal_modules: for m in nasal_modules:
print("pack nasal module:", m) print("pack nasal module:", m)
shutil.copy(m, package_directory.joinpath(m)) shutil.copy(m, package_directory.joinpath(m))
file = tarfile.open(name=tar_file_name + ".tar", mode="w") file = tarfile.open(name=tar_file_name + ".tar", mode="w")
file.add(package_directory) file.add(package_directory)
print("pack succeeded") print("pack succeeded")
file.close() file.close()