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

View File

@ -2,7 +2,6 @@
<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)
[![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)
@ -13,7 +12,8 @@
## __Contents__
* [__Introduction__](#introduction)
* [__Compile__](#how-to-compile)
* [__Download__](#download)
* [__Compile__](#compile)
* [__Usage__](#how-to-use)
* [__Tutorial__](./doc/tutorial.md)
* [__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)
is an ECMAscript-like language used in [FlightGear](https://www.flightgear.org/).
The designer is [Andy Ross](https://github.com/andyross).
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).
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?__
@ -64,7 +63,17 @@ interesting programs and run them without the lib of Flightgear.
You could add your own modules to make
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)
![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>
![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)
[![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)
@ -13,6 +12,7 @@
## __目录__
* [__简介__](#简介)
* [__下载__](#下载)
* [__编译__](#编译)
* [__使用方法__](#使用方法)
* [__教程__](../doc/tutorial_zh.md)
@ -42,18 +42,28 @@ __如果有好的意见或建议欢迎联系我们!__
[Nasal](http://wiki.flightgear.org/Nasal_scripting_language)
是一款语法与 ECMAscript 相似的编程语言,并作为脚本语言被著名开源飞行模拟器 [FlightGear](https://www.flightgear.org/) 所使用。
该语言的设计者为 [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
test:nasal
@ ./nasal -t -d test/andy_gc_test.nas
@ ./nasal test/argparse_test.nas
@ ./nasal -e test/ascii-art.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) {
if (!size) {
return nas_err("quick_fib","lack arguments");
return nas_err("quick_fib", "lack arguments");
}
double num = args[0].to_num();
if (num<2) {
@ -53,7 +53,9 @@ struct ghost_obj {
void ghost_for_test_destructor(void* ptr) {
std::cout << "ghost_for_test::destructor (0x";
std::cout << std::hex << reinterpret_cast<u64>(ptr) << std::dec << ") {\n";
delete static_cast<ghost_obj*>(ptr);
std::cout << " delete 0x" << std::hex;
std::cout << reinterpret_cast<u64>(ptr) << std::dec << ";\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) {
std::cout << "ghost_for_test::mark (0x";
std::cout << std::hex << reinterpret_cast<u64>(ptr) << std::dec << ") {\n";
bfs_queue->push_back(static_cast<ghost_obj*>(ptr)->test_string);
std::cout << " mark 0x" << std::hex;
std::cout << reinterpret_cast<u64>(ptr) << std::dec << "->test_string;\n";
std::cout << "}\n";
@ -86,8 +90,10 @@ var set_new_ghost(var* args, usize size, gc* ngc) {
return nil;
}
f64 num = args[1].num();
reinterpret_cast<ghost_obj*>(res.ghost().pointer)->number = static_cast<u32>(num);
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");
std::cout << "set_new_ghost: successfully set ghost.test_string = just for test\n";
return nil;

View File

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

View File

@ -95,7 +95,8 @@ std::ostream& logo(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;
for(u32 i = 0; i<5; ++i) {
num = (num+rand())*(1.0/(RAND_MAX+1.0));
@ -103,6 +104,7 @@ std::ostream& version(std::ostream& out) {
if (num<0.01) {
nasal::parse::easter_egg();
}
out << "nasal interpreter version " << __nasver__;
out << " " << nasal::get_platform() << " " << nasal::get_arch();
out << " (" << __DATE__ << " " << __TIME__ << ")\n";
@ -150,7 +152,7 @@ void execute(const std::string& file,
}
// 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());
if (cmd&VM_AST) {
nasal::ast_dumper().dump(parse.tree());
@ -166,22 +168,22 @@ void execute(const std::string& file,
}
// run
auto start = clk::now();
const auto start = clk::now();
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);
} 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_limit_mode_flag(cmd&VM_LIMIT);
runtime->run(gen, ld, argv);
}
// get running time
auto end = clk::now();
const auto end = clk::now();
if (cmd&VM_TIME) {
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") {
std::clog << version;
} 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();
} else if (s[0]!='-') {
execute(s, {}, VM_EXEC);
@ -211,7 +213,7 @@ i32 main(i32 argc, const char* argv[]) {
}
// 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},
{"--ast", VM_AST},
{"-a", VM_AST},
@ -233,12 +235,13 @@ i32 main(i32 argc, const char* argv[]) {
{"--ref-file", VM_REF_FILE},
{"--limit", VM_LIMIT|VM_EXEC}
};
u32 cmd = 0;
u32 commands = 0;
std::string filename = "";
std::vector<std::string> vm_argv;
for(i32 i = 1; i<argc; ++i) {
if (cmdlst.count(argv[i])) {
cmd |= cmdlst.at(argv[i]);
if (command_list.count(argv[i])) {
commands |= command_list.at(argv[i]);
} else if (!filename.length()) {
filename = argv[i];
} else {
@ -248,6 +251,7 @@ i32 main(i32 argc, const char* argv[]) {
if (!filename.length()) {
err();
}
execute(filename, vm_argv, cmd? cmd:VM_EXEC);
execute(filename, vm_argv, commands? commands:VM_EXEC);
return 0;
}

View File

@ -1,7 +1,7 @@
#pragma once
#ifndef __nasver__
#define __nasver__ "11.1"
#define __nasver__ "11.2"
#endif
#include <cstdint>
@ -38,7 +38,7 @@ const char* get_platform();
const char* get_arch();
// 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 oct_to_f64(const char*);

View File

@ -8,7 +8,7 @@
namespace nasal {
enum class expr_type: u32 {
enum class expr_type {
ast_null = 0, // null node
ast_use, // use statement
ast_block, // code block
@ -65,13 +65,13 @@ public:
expr(const span& location, expr_type node_type):
nd_loc(location), nd_type(node_type) {}
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_column = column;
}
const span& get_location() const {return nd_loc;}
const u32 get_line() const {return nd_loc.begin_line;}
expr_type get_type() const {return nd_type;}
const auto& get_location() const { return nd_loc; }
const auto get_line() const { return nd_loc.begin_line; }
auto get_type() const { return nd_type; }
void update_location(const span& location) {
nd_loc.end_line = location.end_line;
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)) {
return;
}
u32 size = const_number_map.size();
auto size = const_number_map.size();
const_number_map[num] = size;
const_number_table.push_back(num);
}
@ -79,13 +80,14 @@ void codegen::regist_string(const std::string& str) {
if (const_string_map.count(str)) {
return;
}
u32 size = const_string_map.size();
auto size = const_string_map.size();
const_string_map[str] = size;
const_string_table.push_back(str);
}
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)) {
// check if symbol conflicts with native function name
if (native_function_mapper.count(i.name)) {
@ -93,11 +95,11 @@ void codegen::find_symbol(code_block* node) {
continue;
}
// create new namespace with checking existence of location file
if (!experimental_namespace.count(i.location.file)) {
experimental_namespace[i.location.file] = {};
if (!nasal_namespace.count(i.location.file)) {
nasal_namespace[i.location.file] = {};
}
// 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)) {
scope.insert(i.name);
}
@ -107,41 +109,47 @@ void codegen::find_symbol(code_block* node) {
}
void codegen::regist_symbol(const std::string& name) {
// regist global if local scope list is empty
if (local.empty()) {
if (global.count(name)) {
return;
}
i32 index = global.size();
auto index = global.size();
global[name] = index;
return;
}
if (local.back().count(name)) {
return;
}
i32 index = local.back().size();
auto index = local.back().size();
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()) {
return -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;
}
i32 codegen::upvalue_symbol_find(const std::string& name) {
i64 codegen::upvalue_symbol_find(const std::string& name) {
// 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();
if (size<=1) {
return -1;
}
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)) {
index = ((i<<16)|(*iter).at(name));
}
@ -149,7 +157,7 @@ i32 codegen::upvalue_symbol_find(const std::string& name) {
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({
operation_code,
static_cast<u16>(file_map.at(location.file)),
@ -297,10 +305,14 @@ void codegen::func_gen(function* node) {
block_gen(block);
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();
if (local.back().size()>=STACK_DEPTH) {
if (local.back().size()>=VM_STACK_DEPTH || local.back().size()>=UINT16_MAX) {
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();
@ -346,7 +358,7 @@ void codegen::call_identifier(identifier* node) {
return;
}
i32 index;
i64 index;
if ((index = local_symbol_find(name))>=0) {
emit(op_calll, index, node->get_location());
return;
@ -466,7 +478,7 @@ void codegen::mcall_identifier(identifier* node) {
return;
}
i32 index;
i64 index;
if ((index = local_symbol_find(name))>=0) {
emit(op_mcalll, index, node->get_location());
return;
@ -505,7 +517,7 @@ void codegen::single_def(definition_expr* node) {
const auto& str = node->get_variable_name()->get_name();
calc_gen(node->get_value());
// 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());
}
if (local.empty()) {
@ -518,7 +530,7 @@ void codegen::single_def(definition_expr* node) {
void codegen::multi_def(definition_expr* node) {
auto& identifiers = node->get_variables()->get_variables();
usize size = identifiers.size();
// (var a,b,c) = (c,b,a);
// (var a, b, c) = (c, b, a);
if (node->get_tuple()) {
auto& vals = node->get_tuple()->get_elements();
if (identifiers.size()>vals.size()) {
@ -545,7 +557,7 @@ void codegen::multi_def(definition_expr* node) {
}
return;
}
// (var a,b,c) = [0,1,2];
// (var a, b, c) = [0, 1, 2];
calc_gen(node->get_value());
for(usize i = 0; i<size; ++i) {
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);
if (value_node->get_type()==expr_type::ast_tuple) {
const auto& value_tuple = reinterpret_cast<tuple_expr*>(value_node)
->get_elements();
for(i32 i = size-1; i>=0; --i) {
for(i64 i = size-1; i>=0; --i) {
calc_gen(value_tuple[i]);
}
auto& tuple = tuple_node->get_elements();
for(i32 i = 0; i<size; ++i) {
for(i64 i = 0; i<size; ++i) {
mcall(tuple[i]);
// use load operands to avoid meq's pop operand
// 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];
calc_gen(value_node);
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());
mcall(tuple[i]);
// use load operands to avoid meq's pop operand
// and this operation changes local and global value directly
replace_left_assignment_with_load(tuple[i]->get_location());
}
// pop source vector
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()) {
code[i].num = continue_place;
}
@ -943,7 +956,7 @@ void codegen::statement_generation(expr* node) {
case expr_type::ast_ternary:
calc_gen(node);
// 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_pop, 0, node->get_location());
@ -1254,7 +1267,7 @@ void codegen::block_gen(code_block* node) {
break;
case expr_type::ast_null: break;
case expr_type::ast_id:
if (need_repl_output && local.empty()) {
if (flag_need_repl_output && local.empty()) {
repl_mode_info_output_gen(tmp);
} else {
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_str:
case expr_type::ast_bool:
if (need_repl_output && local.empty()) {
if (flag_need_repl_output && local.empty()) {
repl_mode_info_output_gen(tmp);
}
break;
@ -1313,7 +1326,7 @@ const error& codegen::compile(parse& parse,
linker& import,
bool repl_flag,
bool limit_mode) {
need_repl_output = repl_flag;
flag_need_repl_output = repl_flag;
flag_limited_mode = limit_mode;
init_native_function();
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());
// size out of bound check
if (const_number_table.size()>0xffffff) {
if (const_number_table.size()>INT64_MAX) {
err.err("code",
"too many constant numbers: " +
std::to_string(const_number_table.size())
);
}
if (const_string_table.size()>0xffffff) {
if (const_string_table.size()>INT64_MAX) {
err.err("code",
"too many constant strings: " +
std::to_string(const_string_table.size())
@ -1348,7 +1361,7 @@ const error& codegen::compile(parse& parse,
}
// check global variables size
if (global.size()>=STACK_DEPTH/2) {
if (global.size()>=VM_STACK_DEPTH) {
err.err("code",
"too many global variables: " +
std::to_string(global.size())
@ -1356,7 +1369,7 @@ const error& codegen::compile(parse& parse,
}
// check generated code size
if (code.size()>0xffffff) {
if (code.size()>INT64_MAX) {
err.err("code",
"bytecode size overflow: " +
std::to_string(code.size())
@ -1367,8 +1380,8 @@ const error& codegen::compile(parse& parse,
void codegen::print(std::ostream& out) {
// func end stack, reserved for code print
std::stack<u32> func_begin_stack;
std::stack<u32> func_end_stack;
std::stack<u64> func_begin_stack;
std::stack<u64> func_end_stack;
// print const numbers
for(auto num : const_number_table) {
@ -1391,7 +1404,8 @@ void codegen::print(std::ostream& out) {
const_string_table.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
const auto& c = code[i];
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
if (c.op==op_newf) {
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) {
func_begin_stack.push(i);
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 {
for(const auto& domain : experimental_namespace) {
for(const auto& domain : nasal_namespace) {
out << "<" << domain.first << ">\n";
for(const auto& i : domain.second) {
out << " 0x" << std::setw(4) << std::setfill('0');

View File

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

View File

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

View File

@ -140,9 +140,9 @@ private:
private:
bool next;
usize fsize;
usize file_list_size;
u16 break_file_index;
u32 break_line;
u64 break_line;
error src;
private:
@ -152,24 +152,21 @@ private:
private:
std::vector<std::string> parse(const std::string&);
u16 file_index(const std::string&) const;
void err();
void help();
void err() const;
void help() const;
void list_file() const;
void step_info();
void interact();
public:
dbg():
next(true), fsize(0),
break_file_index(0), break_line(0),
do_operand_count(false) {}
void run(
const codegen&,
const linker&,
const std::vector<std::string>&,
bool,
bool
);
dbg(): next(true), file_list_size(0),
break_file_index(0), break_line(0),
do_operand_count(false) {}
void run(const codegen&,
const linker&,
const std::vector<std::string>&,
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";
}
void error::err(
const std::string& stage, const span& loc, const std::string& info) {
void error::err(const std::string& stage,
const span& loc,
const std::string& info) {
// load error occurred file into string lines
load(loc.file);
++cnt;
std::cerr
<< red << stage << ": " << white << info << reset << "\n" << cyan << " --> "
<< red << loc.file << ":" << loc.begin_line << ":" << loc.begin_column+1
<< reset << "\n";
std::cerr << red << stage << ": " << white << info << reset << "\n";
std::cerr << cyan << " --> " << red;
loc.dump_begin(std::cerr);
std::cerr << reset << "\n";
const usize maxlen = std::to_string(loc.end_line).length();
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
if (!line) {
continue;
@ -164,25 +165,25 @@ void error::err(
// output underline
std::cerr << cyan << iden << " | " << reset;
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']);
}
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;
}
} 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']);
}
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;
}
} 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'? "^^^^":"^");
}
} 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'? "^^^^":"^");
}
}

View File

@ -11,11 +11,15 @@
namespace nasal {
struct span {
u32 begin_line;
u32 begin_column;
u32 end_line;
u32 end_column;
u64 begin_line;
u64 begin_column;
u64 end_line;
u64 end_column;
std::string file;
void dump_begin(std::ostream& out) const {
out << file << ":" << begin_line << ":" << begin_column + 1;
}
};
std::ostream& back_white(std::ostream&);
@ -46,7 +50,7 @@ private:
std::string identation(usize 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);
while(tmp.length()<len) {
tmp = " "+tmp;
@ -65,7 +69,7 @@ public:
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() {
std::vector<var> 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();
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);
@ -35,6 +38,7 @@ void gc::mark() {
return;
}
// normal mark
while(!bfs.empty()) {
var value = bfs.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);
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
nas_val* tmp = new nas_val(type);
@ -197,14 +201,13 @@ void gc::extend(const vm_type type) {
memory.push_back(tmp);
unused[index].push_back(tmp);
}
// if incr[index] = 1, this will always be 1
incr[index] = incr[index]+incr[index]/2;
}
void gc::init(
const std::vector<std::string>& constant_strings,
const std::vector<std::string>& argv
) {
void gc::init(const std::vector<std::string>& constant_strings,
const std::vector<std::string>& argv) {
// initialize counters
worktime = 0;
for(u8 i = 0; i<gc_type_size; ++i) {
@ -216,25 +219,25 @@ void gc::init(
// init constant strings
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
if (strs[i].is_str() && strs[i].str()==constant_strings[i]) {
continue;
}
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];
}
// record arguments
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
if (env_argv[i].is_str() && env_argv[i].str()==argv[i]) {
continue;
}
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];
}
}
@ -315,7 +318,7 @@ void gc::info() const {
if (!gcnt[i] && !acnt[i] && !size[i]) {
continue;
}
total += gcnt[i];
total += static_cast<f64>(gcnt[i]);
std::clog << " " << left << setw(indent) << setfill(' ') << name[i];
std::clog << " | " << left << setw(indent) << setfill(' ') << gcnt[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 << " " << left << setw(indent) << setfill(' ') << "max sweep";
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";
}

View File

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

View File

@ -41,8 +41,8 @@ std::string linker::get_path(expr* node) {
return content->get_content();
}
std::string linker::find_real_file_path(
const std::string& filename, const span& location) {
std::string linker::find_real_file_path(const std::string& filename,
const span& location) {
// first add file name itself into the file path
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) {
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 value = new hash_expr(block->get_location());
result->set_value(value);
@ -406,6 +406,13 @@ const error& linker::link(parse& parse, bool spath = false) {
merge_tree(library, parse.tree());
// swap tree root, and delete old root
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;
}

View File

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

View File

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

View File

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

View File

@ -8,100 +8,100 @@
namespace nasal {
enum op_code_type: u8 {
op_exit, // stop the virtual machine
op_repl, // in repl mode: print value on stack top
op_intl, // local scope size
op_loadg, // load global value
op_loadl, // load local value
op_loadu, // load upvalue
op_pnum, // push constant number to the stack
op_pnil, // push constant nil to the stack
op_pstr, // push constant std::string to the stack
op_newv, // push new vector with initial values from stack
op_newh, // push new hash to the stack
op_newf, // push new function to the stack
op_happ, // hash append
op_para, // normal parameter
op_deft, // default parameter
op_dyn, // dynamic parameter
op_lnot, // ! logical negation
op_usub, // - negation
op_bnot, // ~ bitwise not static_cast<i32>
op_btor, // | bitwise or
op_btxor, // ^ bitwise xor
op_btand, // & bitwise and
op_add, // +
op_sub, // -
op_mul, // *
op_div, // /
op_lnk, // ~
op_addc, // + const
op_subc, // - const
op_mulc, // * const
op_divc, // / const
op_lnkc, // ~ const
op_addeq, // += maybe pop stack top
op_subeq, // -= maybe pop stack top
op_muleq, // *= maybe pop stack top
op_diveq, // /= maybe pop stack top
op_lnkeq, // ~= maybe pop stack top
op_btandeq,// &= maybe pop stack top
op_btoreq, // |= maybe pop stack top
op_btxoreq,// ^= maybe pop stack top
op_addeqc, // += const don't pop stack top
op_subeqc, // -= const don't pop stack top
op_muleqc, // *= const don't pop stack top
op_diveqc, // /= const don't pop stack top
op_lnkeqc, // ~= const don't pop stack top
op_addecp, // += const and pop stack top
op_subecp, // -= const and pop stack top
op_mulecp, // *= const and pop stack top
op_divecp, // /= const and pop stack top
op_lnkecp, // ~= concat const std::string and pop stack top
op_meq, // = maybe pop stack top
op_eq, // == compare operator
op_neq, // != compare operator
op_less, // < compare operator
op_leq, // <= compare operator
op_grt, // > compare operator
op_geq, // >= compare operator
op_lessc, // < const compare operator
op_leqc, // <= const compare operator
op_grtc, // > const compare operator
op_geqc, // >= const compare operator
op_pop, // pop a value out of stack top
op_jmp, // jump absolute address with no condition
op_jt, // used in operator and/or,jmp when condition is true and DO NOT POP
op_jf, // used in conditional/loop,jmp when condition is false and POP STACK
op_cnt, // add counter for forindex/foreach
op_findex, // index counter on the top of forindex_stack plus 1
op_feach, // index counter on the top of forindex_stack plus 1 and get the value in vector
op_callg, // get value in global scope
op_calll, // get value in local scope
op_upval, // get value in closure, high 16 as the index of upval, low 16 as the index of local
op_callv, // call vec[index]
op_callvi, // call vec[immediate] (used in multi-assign/multi-define)
op_callh, // call hash.label
op_callfv, // call function(vector as parameters)
op_callfh, // call function(hash as parameters)
op_callb, // call native functions
op_slcbeg, // begin of slice like: vec[1, 2, 3:6, 0, -1]
op_slcend, // end of slice
op_slc, // slice like vec[1]
op_slc2, // slice like vec[nil:10]
op_mcallg, // get memory space of value in global scope
op_mcalll, // get memory space of value in local scope
op_mupval, // get memory space of value in closure
op_mcallv, // get memory space of vec[index]
op_mcallh, // get memory space of hash.label
op_ret // return
op_exit, // stop the virtual machine
op_repl, // in repl mode: print value on stack top
op_intl, // local scope size
op_loadg, // load global value
op_loadl, // load local value
op_loadu, // load upvalue
op_pnum, // push constant number to the stack
op_pnil, // push constant nil to the stack
op_pstr, // push constant std::string to the stack
op_newv, // push new vector with initial values from stack
op_newh, // push new hash to the stack
op_newf, // push new function to the stack
op_happ, // hash append
op_para, // normal parameter
op_deft, // default parameter
op_dyn, // dynamic parameter
op_lnot, // ! logical negation
op_usub, // - negation
op_bnot, // ~ bitwise not static_cast<i32>
op_btor, // | bitwise or
op_btxor, // ^ bitwise xor
op_btand, // & bitwise and
op_add, // +
op_sub, // -
op_mul, // *
op_div, // /
op_lnk, // ~
op_addc, // + const
op_subc, // - const
op_mulc, // * const
op_divc, // / const
op_lnkc, // ~ const
op_addeq, // += maybe pop stack top
op_subeq, // -= maybe pop stack top
op_muleq, // *= maybe pop stack top
op_diveq, // /= maybe pop stack top
op_lnkeq, // ~= maybe pop stack top
op_btandeq, // &= maybe pop stack top
op_btoreq, // |= maybe pop stack top
op_btxoreq, // ^= maybe pop stack top
op_addeqc, // += const don't pop stack top
op_subeqc, // -= const don't pop stack top
op_muleqc, // *= const don't pop stack top
op_diveqc, // /= const don't pop stack top
op_lnkeqc, // ~= const don't pop stack top
op_addecp, // += const and pop stack top
op_subecp, // -= const and pop stack top
op_mulecp, // *= const and pop stack top
op_divecp, // /= const and pop stack top
op_lnkecp, // ~= concat const std::string and pop stack top
op_meq, // = maybe pop stack top
op_eq, // == compare operator
op_neq, // != compare operator
op_less, // < compare operator
op_leq, // <= compare operator
op_grt, // > compare operator
op_geq, // >= compare operator
op_lessc, // < const compare operator
op_leqc, // <= const compare operator
op_grtc, // > const compare operator
op_geqc, // >= const compare operator
op_pop, // pop a value out of stack top
op_jmp, // jump absolute address with no condition
op_jt, // used in operator and/or, jmp when condition is true and DO NOT POP
op_jf, // used in conditional/loop, jmp when condition is false and POP STACK
op_cnt, // add counter for forindex/foreach
op_findex, // index counter on the top of forindex_stack plus 1
op_feach, // index counter on the top of forindex_stack plus 1 and get the value in vector
op_callg, // get value in global scope
op_calll, // get value in local scope
op_upval, // get value in closure, high 16 as the index of upval, low 16 as the index of local
op_callv, // call vec[index]
op_callvi, // call vec[immediate] (used in multi-assign/multi-define)
op_callh, // call hash.label
op_callfv, // call function(vector as parameters)
op_callfh, // call function(hash as parameters)
op_callb, // call native functions
op_slcbeg, // begin of slice like: vec[1, 2, 3:6, 0, -1]
op_slcend, // end of slice
op_slc, // slice like vec[1]
op_slc2, // slice like vec[nil:10]
op_mcallg, // get memory space of value in global scope
op_mcalll, // get memory space of value in local scope
op_mupval, // get memory space of value in closure
op_mcallv, // get memory space of vec[index]
op_mcallh, // get memory space of hash.label
op_ret // return
};
struct opcode {
u8 op; // opcode
u16 fidx; // source code file index
u32 num; // immediate num
u32 line; // location line of source code
u64 num; // immediate num
u64 line; // location line of source code
opcode() = default;
opcode(const opcode&) = default;
opcode& operator=(const opcode&) = default;
@ -110,24 +110,23 @@ struct opcode {
class codestream {
private:
opcode code;
const u32 index;
const u64 index;
inline static const f64* const_number = nullptr;
inline static const std::string* const_string = nullptr;
inline static const nasal_builtin_table* natives = nullptr;
inline static const std::string* files = nullptr;
public:
codestream(const opcode& c, const u32 i): code(c), index(i) {}
static void set(
const f64*, const std::string*,
const nasal_builtin_table*,
const std::string* file_list = nullptr
);
codestream(const opcode& c, const u64 i): code(c), index(i) {}
static void set(const f64*,
const std::string*,
const nasal_builtin_table*,
const std::string* file_list = nullptr);
void dump(std::ostream&) const;
};
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() {
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) {
switch(toks[check_ptr].type) {
case tok::lcurve: ++curve; break;
@ -156,8 +156,8 @@ bool parse::check_in_curve_multi_definition() {
}
bool parse::check_special_call() {
// special call means like this: function_name(a:1,b:2,c:3);
u32 check_ptr = ptr, curve = 1, bracket = 0, brace = 0;
// special call means like this: function_name(a:1, b:2, c:3);
u64 check_ptr = ptr, curve = 1, bracket = 0, brace = 0;
while(toks[++check_ptr].type!=tok::eof && curve) {
switch(toks[check_ptr].type) {
case tok::lcurve: ++curve; break;
@ -254,11 +254,11 @@ vector_expr* parse::vec() {
// panic set for this token is not ','
// this is the FIRST set of calculation
// array end with tok::null=0
const tok panic[]={
tok::id,tok::str,tok::num,tok::tktrue,
tok::tkfalse,tok::opnot,tok::sub,tok::tknil,
tok::func,tok::var,tok::lcurve,tok::floater,
tok::lbrace,tok::lbracket,tok::null
const tok panic[] = {
tok::id, tok::str, tok::num, tok::tktrue,
tok::tkfalse, tok::opnot, tok::sub, tok::nil,
tok::func, tok::var, tok::lcurve, tok::floater,
tok::lbrace, tok::lbracket, tok::null
};
auto node = new vector_expr(toks[ptr].loc);
match(tok::lbracket);
@ -372,7 +372,7 @@ expr* parse::expression() {
}
switch(type) {
case tok::use: return use_stmt_gen();
case tok::tknil:
case tok::nil:
case tok::num:
case tok::str:
case tok::id:
@ -637,9 +637,9 @@ unary_operator* parse::unary() {
expr* parse::scalar() {
expr* node = nullptr;
if (lookahead(tok::tknil)) {
if (lookahead(tok::nil)) {
node = nil();
match(tok::tknil);
match(tok::nil);
} else if (lookahead(tok::num)) {
node = num();
} else if (lookahead(tok::str)) {
@ -713,11 +713,11 @@ call_vector* parse::callv() {
// panic set for this token is not ','
// this is the FIRST set of subvec
// array end with tok::null=0
const tok panic[]={
tok::id,tok::str,tok::num,tok::tktrue,
tok::tkfalse,tok::opnot,tok::sub,tok::tknil,
tok::func,tok::var,tok::lcurve,tok::floater,
tok::lbrace,tok::lbracket,tok::colon,tok::null
const tok panic[] = {
tok::id, tok::str, tok::num, tok::tktrue,
tok::tkfalse, tok::opnot, tok::sub, tok::nil,
tok::func, tok::var, tok::lcurve, tok::floater,
tok::lbrace, tok::lbracket, tok::colon, tok::null
};
auto node = new call_vector(toks[ptr].loc);
match(tok::lbracket);
@ -743,11 +743,11 @@ call_function* parse::callf() {
// panic set for this token is not ','
// this is the FIRST set of calculation/hashmember
// array end with tok::null=0
const tok panic[]={
tok::id,tok::str,tok::num,tok::tktrue,
tok::tkfalse,tok::opnot,tok::sub,tok::tknil,
tok::func,tok::var,tok::lcurve,tok::floater,
tok::lbrace,tok::lbracket,tok::null
const tok panic[] = {
tok::id, tok::str, tok::num, tok::tktrue,
tok::tkfalse, tok::opnot, tok::sub, tok::nil,
tok::func, tok::var, tok::lcurve, tok::floater,
tok::lbrace, tok::lbracket, tok::null
};
auto node = new call_function(toks[ptr].loc);
bool special_call=check_special_call();
@ -802,7 +802,7 @@ expr* parse::definition() {
}
multi_identifier* parse::incurve_def() {
const auto& loc=toks[ptr].loc;
const auto& loc = toks[ptr].loc;
match(tok::lcurve);
match(tok::var);
auto node = multi_id();
@ -813,7 +813,7 @@ multi_identifier* parse::incurve_def() {
}
multi_identifier* parse::outcurve_def() {
const auto& loc=toks[ptr].loc;
const auto& loc = toks[ptr].loc;
match(tok::lcurve);
auto node = multi_id();
update_location(node);
@ -840,12 +840,13 @@ multi_identifier* parse::multi_id() {
}
tuple_expr* parse::multi_scalar() {
// if check_call_memory is true,we will check if value called here can reach a memory space
const tok panic[]={
tok::id,tok::str,tok::num,tok::tktrue,
tok::tkfalse,tok::opnot,tok::sub,tok::tknil,
tok::func,tok::var,tok::lcurve,tok::floater,
tok::lbrace,tok::lbracket,tok::null
// if check_call_memory is true,
// we will check if value called here can reach a memory space
const tok panic[] = {
tok::id, tok::str, tok::num, tok::tktrue,
tok::tkfalse, tok::opnot, tok::sub, tok::nil,
tok::func, tok::var, tok::lcurve, tok::floater,
tok::lbrace, tok::lbracket, tok::null
};
auto node = new tuple_expr(toks[ptr].loc);
match(tok::lcurve);
@ -1069,7 +1070,7 @@ return_expr* parse::return_expression() {
auto node = new return_expr(toks[ptr].loc);
match(tok::ret);
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::func || type==tok::sub ||
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)
private:
u32 ptr;
u32 in_func; // count function block
u32 in_loop; // count loop block
u64 ptr;
u64 in_func; // count function block
u64 in_loop; // count loop block
const token* toks;
code_block* root;
error err;
@ -37,7 +37,7 @@ private:
{tok::rif ,"if" },
{tok::elsif ,"elsif" },
{tok::relse ,"else" },
{tok::tknil ,"nil" },
{tok::nil ,"nil" },
{tok::lcurve ,"(" },
{tok::rcurve ,")" },
{tok::lbracket,"[" },

View File

@ -42,6 +42,7 @@ var nas_hash::get_value(const std::string& key) {
} else if (!elems.count("parents")) {
return var::none();
}
auto ret = var::none();
auto& val = elems.at("parents");
if (!val.is_vec()) {
@ -64,12 +65,14 @@ var* nas_hash::get_memory(const std::string& key) {
} else if (!elems.count("parents")) {
return nullptr;
}
var* addr = nullptr;
var& val = elems.at("parents");
if (!val.is_vec()) {
return addr;
}
for(auto& i : val.vec().elems) {
// recursively search key in `parents`
if (i.is_hash()) {
addr = i.hash().get_memory(key);
}
@ -95,6 +98,30 @@ std::ostream& operator<<(std::ostream& out, nas_hash& hash) {
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() {
dynamic_parameter_index = -1;
local.clear();
@ -102,11 +129,10 @@ void nas_func::clear() {
keys.clear();
}
void nas_ghost::set(
const std::string& ghost_type_name,
destructor destructor_pointer,
marker gc_marker_pointer,
void* ghost_pointer) {
void nas_ghost::set(const std::string& ghost_type_name,
destructor destructor_pointer,
marker gc_marker_pointer,
void* ghost_pointer) {
type_name = ghost_type_name;
destructor_function = destructor_pointer;
gc_mark_function = gc_marker_pointer;
@ -145,14 +171,14 @@ void nas_co::clear() {
if (!ctx.stack) {
return;
}
for(u32 i = 0; i<STACK_DEPTH; ++i) {
for(u32 i = 0; i<VM_STACK_DEPTH; ++i) {
ctx.stack[i] = var::nil();
}
ctx.pc = 0;
ctx.localr = nullptr;
ctx.memr = nullptr;
ctx.canary = ctx.stack+STACK_DEPTH-1;
ctx.canary = ctx.stack+VM_STACK_DEPTH-1;
ctx.top = ctx.stack;
ctx.funcr = 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) {
mark = gc_status::collected;
type = val_type;
unmutable = 0;
immutable = 0;
switch(val_type) {
case vm_type::vm_str: ptr.str = new std::string; break;
case vm_type::vm_vec: ptr.vec = new nas_vec; break;
@ -214,14 +240,14 @@ nas_val::nas_val(vm_type val_type) {
nas_val::~nas_val() {
switch(type) {
case vm_type::vm_str: delete ptr.str; break;
case vm_type::vm_vec: delete ptr.vec; break;
case vm_type::vm_hash: delete ptr.hash; break;
case vm_type::vm_func: delete ptr.func; break;
case vm_type::vm_upval:delete ptr.upval; break;
case vm_type::vm_ghost:delete ptr.obj; break;
case vm_type::vm_co: delete ptr.co; break;
case vm_type::vm_map: delete ptr.map; break;
case vm_type::vm_str: delete ptr.str; break;
case vm_type::vm_vec: delete ptr.vec; break;
case vm_type::vm_hash: delete ptr.hash; break;
case vm_type::vm_func: delete ptr.func; break;
case vm_type::vm_upval: delete ptr.upval; break;
case vm_type::vm_ghost: delete ptr.obj; break;
case vm_type::vm_co: delete ptr.co; break;
case vm_type::vm_map: delete ptr.map; break;
default: break;
}
type = vm_type::vm_nil;
@ -229,14 +255,14 @@ nas_val::~nas_val() {
void nas_val::clear() {
switch(type) {
case vm_type::vm_str: ptr.str->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_func: ptr.func->clear(); break;
case vm_type::vm_upval:ptr.upval->clear(); break;
case vm_type::vm_ghost:ptr.obj->clear(); break;
case vm_type::vm_co: ptr.co->clear(); break;
case vm_type::vm_map: ptr.map->clear(); break;
case vm_type::vm_str: ptr.str->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_func: ptr.func->clear(); break;
case vm_type::vm_upval: ptr.upval->clear(); break;
case vm_type::vm_ghost: ptr.obj->clear(); break;
case vm_type::vm_co: ptr.co->clear(); break;
case vm_type::vm_map: ptr.map->clear(); break;
default: break;
}
}
@ -254,21 +280,24 @@ std::string var::to_str() {
tmp.erase(tmp.find_last_not_of('.')+1, std::string::npos);
return tmp;
}
return "";
std::stringstream ss;
ss << *this;
return ss.str();
}
std::ostream& operator<<(std::ostream& out, var& ref) {
switch(ref.type) {
case vm_type::vm_none: out << "undefined"; break;
case vm_type::vm_nil: out << "nil"; break;
case vm_type::vm_num: out << ref.val.num; break;
case vm_type::vm_str: out << ref.str(); break;
case vm_type::vm_vec: out << ref.vec(); break;
case vm_type::vm_hash: out << ref.hash(); break;
case vm_type::vm_func: out << "func(..) {..}"; break;
case vm_type::vm_ghost:out << ref.ghost(); break;
case vm_type::vm_co: out << ref.co(); break;
case vm_type::vm_map: out << ref.map(); break;
case vm_type::vm_none: out << "undefined"; break;
case vm_type::vm_nil: out << "nil"; break;
case vm_type::vm_num: out << ref.val.num; break;
case vm_type::vm_str: out << ref.str(); break;
case vm_type::vm_vec: out << ref.vec(); break;
case vm_type::vm_hash: out << ref.hash(); break;
case vm_type::vm_func: out << ref.func(); break;
case vm_type::vm_ghost: out << ref.ghost(); break;
case vm_type::vm_co: out << ref.co(); break;
case vm_type::vm_map: out << ref.map(); break;
default: break;
}
return out;
@ -279,14 +308,14 @@ bool var::object_check(const std::string& name) {
}
var var::none() {
return {vm_type::vm_none, static_cast<u32>(0)};
return {vm_type::vm_none, static_cast<u64>(0)};
}
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};
}
@ -310,7 +339,7 @@ var* var::addr() {
return val.addr;
}
u32 var::ret() const {
u64 var::ret() const {
return val.ret;
}
@ -355,7 +384,7 @@ nas_map& var::map() {
}
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();
}

View File

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

View File

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

View File

@ -20,13 +20,15 @@ namespace nasal {
class vm {
protected:
/* registers of vm */
context ctx; // running context
/* vm context */
context ctx;
/* constants */
const f64* const_number = nullptr; // constant numbers
const std::string* const_string = nullptr; // constant symbols and strings
std::vector<u32> imm; // immediate number table
const f64* const_number = nullptr;
const std::string* const_string = nullptr;
std::vector<u64> imm; // immediate number table
/* nasal native functions */
std::vector<nasal_builtin_table> native_function;
/* garbage collector */
@ -49,15 +51,13 @@ protected:
bool flag_limited_mode = false;
/* vm initializing function */
void init(
const std::vector<std::string>&,
const std::vector<f64>&,
const std::vector<nasal_builtin_table>&,
const std::vector<opcode>&,
const std::unordered_map<std::string, i32>&,
const std::vector<std::string>&,
const std::vector<std::string>&
);
void vm_init_enrty(const std::vector<std::string>&,
const std::vector<f64>&,
const std::vector<nasal_builtin_table>&,
const std::vector<opcode>&,
const std::unordered_map<std::string, u64>&,
const std::vector<std::string>&,
const std::vector<std::string>&);
void context_and_global_init();
/* debug functions */
@ -66,7 +66,7 @@ protected:
void function_detail_info(const nas_func&);
void function_call_trace();
void trace_back();
void stack_info(const u32);
void stack_info(const u64);
void register_info();
void global_state();
void local_state();
@ -79,9 +79,11 @@ protected:
std::string type_name_string(const var&) const;
void die(const std::string&);
protected:
/* vm calculation functions*/
inline bool cond(var&);
protected:
/* vm operands */
inline void o_repl();
inline void o_intl();
@ -174,8 +176,8 @@ public:
/* constructor of vm instance */
vm() {
ctx.stack = new var[STACK_DEPTH];
global = new var[STACK_DEPTH];
ctx.stack = new var[VM_STACK_DEPTH];
global = new var[VM_STACK_DEPTH];
}
~vm() {
delete[] ctx.stack;
@ -183,11 +185,9 @@ public:
}
/* execution entry */
void run(
const codegen&, // get generated code
const linker&, // get list of used files
const std::vector<std::string>& // get arguments input by command line
);
void run(const codegen&, // get generated code
const linker&, // get list of used files
const std::vector<std::string>&); // get command line arguments
/* set detail report info flag */
void set_detail_report_info(bool flag) {verbose = flag;}
@ -251,8 +251,9 @@ inline void vm::o_newv() {
auto& vec = newv.vec().elems;
vec.resize(imm[ctx.pc]);
// use top-=imm[pc]-1 here will cause error if imm[pc] is 0
ctx.top = ctx.top-imm[ctx.pc]+1;
for(u32 i = 0; i<imm[ctx.pc]; ++i) {
ctx.top = ctx.top - imm[ctx.pc] + 1;
for(u64 i = 0; i<imm[ctx.pc]; ++i) {
vec[i] = ctx.top[i];
}
ctx.top[0] = newv;
@ -270,16 +271,21 @@ inline void vm::o_newf() {
/* this means you create a new function in local scope */
if (ctx.localr) {
// copy upval scope list from upper level function
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())?
ngc.alloc(vm_type::vm_upval):
ctx.upvalr;
upval.upval().size = ctx.funcr.func().local_size;
upval.upval().stack_frame_offset = ctx.localr;
// 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().stack_frame_offset = ctx.localr;
ctx.upvalr = upval;
}
func.upval.push_back(upval);
ctx.upvalr = upval;
}
}
@ -305,6 +311,7 @@ inline void vm::o_deft() {
inline void vm::o_dyn() {
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() {
@ -669,7 +676,7 @@ inline void vm::o_callvi() {
die("must use a vector but get "+type_name_string(val));
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]);
if (ctx.top[0].is_none()) {
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));
return;
}
const auto& str = const_string[imm[ctx.pc]];
if (val.is_hash()) {
ctx.top[0] = val.hash().get_value(str);
@ -700,7 +708,7 @@ inline void vm::o_callh() {
}
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
if (!local[-1].is_func()) {
die("must call a function but get "+type_name_string(local[-1]));
@ -719,7 +727,7 @@ inline void vm::o_callfv() {
return;
}
// 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()) {
die(report_lack_arguments(argc, func));
return;
@ -730,16 +738,17 @@ inline void vm::o_callfv() {
if (func.dynamic_parameter_index>=0) {
// load dynamic argument
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]);
}
} else if (parameter_size<argc) {
// load arguments to default dynamic argument "arg", located at stack+1
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]);
}
}
// should reset stack top after allocating vector
// because this may cause gc
// then all the available values the vector needs
@ -747,14 +756,18 @@ inline void vm::o_callfv() {
// collected incorrectly
ctx.top = local+func.local_size;
const u32 min_size = (std::min)(parameter_size, argc); // avoid error in MSVC
for(u32 i = min_size; i>=1; --i) { // load arguments
// use (std::min) to avoid compilation error in MSVC
// 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[0] = func.local[0];// load "me"
local[0] = func.local[0]; // load "me"
// 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];
}
// load dynamic argument
@ -967,25 +980,31 @@ inline void vm::o_mcallv() {
}
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()) {
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;
}
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()) {
ctx.memr = hash.map().get_memory(str);
ctx.memr = hash.map().get_memory(key);
if (!ctx.memr) {
die("cannot find symbol \"" + str + "\"");
die("cannot find symbol \"" + key + "\"");
}
return;
}
// call hash member
auto& ref = hash.hash();
ctx.memr = ref.get_memory(str);
// create a new key
ctx.memr = ref.get_memory(key);
// create a new key if not exists
if (!ctx.memr) {
ref.elems[str] = nil;
ctx.memr = ref.get_memory(str);
ref.elems[key] = nil;
ctx.memr = ref.get_memory(key);
}
}
@ -1023,7 +1042,7 @@ inline void vm::o_ret() {
auto size = func.func().local_size;
upval.on_stack = false;
upval.elems.resize(size);
for(u32 i = 0; i<size; ++i) {
for(u64 i = 0; i<size; ++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) {
// bits.fld(s,0,3);
// bits.fld(s, 0, 3);
// if s stores 10100010(162)
// will get 101(5)
auto local = ctx->localr;
auto str = local[1];
auto startbit = local[2];
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");
}
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) {
// bits.sfld(s,0,3);
// bits.sfld(s, 0, 3);
// if s stores 10100010(162)
// will get 101(5) then this will be signed extended to
// 11111101(-3)
@ -78,7 +78,7 @@ var builtin_sfld(context* ctx, gc* ngc) {
auto str = local[1];
auto startbit = local[2];
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");
}
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) {
// bits.setfld(s,0,8,69);
// bits.setfld(s, 0, 8, 69);
// set 01000101(69) to string will get this:
// 10100010(162)
// so s[0]=162
@ -112,7 +112,7 @@ var builtin_setfld(context* ctx, gc* ngc) {
auto startbit = local[2];
auto length = local[3];
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");
}
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)) {
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");
}
if (!length.is_num()) {
@ -102,7 +102,7 @@ var builtin_read(context* ctx, gc* ngc) {
static_cast<FILE*>(file_descriptor.ghost().pointer)
);
buffer.str() = temp_buffer;
buffer.val.gcobj->unmutable = true;
buffer.val.gcobj->immutable = true;
delete []temp_buffer;
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) {
auto val = ctx->localr[1];
f64 num = 0;
usize num = 0;
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_vec: num = val.vec().size(); break;
case vm_type::vm_hash: num = val.hash().size(); break;
case vm_type::vm_map: num = val.map().mapper.size(); break;
default: break;
}
return var::num(num);
return var::num(static_cast<f64>(num));
}
var builtin_time(context* ctx, gc* ngc) {
@ -323,7 +324,7 @@ var builtin_substr(context* ctx, gc* ngc) {
if (begin>=str.str().length()) {
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) {
@ -339,6 +340,7 @@ var builtin_left(context* ctx, gc* ngc) {
auto local = ctx->localr;
var str = local[1];
var len = local[2];
if (!str.is_str()) {
return nas_err("left", "\"string\" must be string");
}
@ -355,20 +357,23 @@ var builtin_right(context* ctx, gc* ngc) {
auto local = ctx->localr;
var str = local[1];
var len = local[2];
if (!str.is_str()) {
return nas_err("right", "\"string\" must be string");
}
if (!len.is_num()) {
return nas_err("right", "\"length\" must be number");
}
i32 length = static_cast<i32>(len.num());
i32 srclen = str.str().length();
i32 srclen = static_cast<i32>(str.str().length());
if (length>srclen) {
length = srclen;
}
if (length<0) {
length = 0;
}
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) {
const char* extend[] = {
""," ","","ƒ","","","","",
"ˆ","","Š","","Œ"," ","Ž"," ",
" ","","","","","","","",
"˜","","š","","œ"," ","ž","Ÿ",
" ","¡","¢","£","¤","¥","¦","§",
"¨","©","ª","«","¬"," ","®","¯",
"°","±","²","³","´","µ","","·",
"¸","¹","º","»","¼","½","¾","¿",
"À","Á","Â","Ã","Ä","Å","Æ","Ç",
"È","É","Ê","Ë","Ì","Í","Î","Ï",
"Ð","Ñ","Ò","Ó","Ô","Õ","Ö","×",
"Ø","Ù","Ú","Û","Ü","Ý","Þ","ß",
"à","á","â","ã","ä","å","æ","ç",
"è","é","ê","ë","ì","í","î","ï",
"ð","ñ","ò","ó","ô","õ","ö","÷",
"ø","ù","ú","û","ü","ý","þ","ÿ"
"", " ", "", "ƒ", "", "", "", "",
"ˆ", "", "Š", "", "Œ", " ", "Ž", " ",
" ", "", "", "", "", "", "", "",
"˜", "", "š", "", "œ", " ", "ž", "Ÿ",
" ", "¡", "¢", "£", "¤", "¥", "¦", "§",
"¨", "©", "ª", "«", "¬", " ", "®", "¯",
"°", "±", "²", "³", "´", "µ", "", "·",
"¸", "¹", "º", "»", "¼", "½", "¾", "¿",
"À", "Á", "Â", "Ã", "Ä", "Å", "Æ", "Ç",
"È", "É", "Ê", "Ë", "Ì", "Í", "Î", "Ï",
"Ð", "Ñ", "Ò", "Ó", "Ô", "Õ", "Ö", "×",
"Ø", "Ù", "Ú", "Û", "Ü", "Ý", "Þ", "ß",
"à", "á", "â", "ã", "ä", "å", "æ", "ç",
"è", "é", "ê", "ë", "ì", "í", "î", "ï",
"ð", "ñ", "ò", "ó", "ô", "õ", "ö", "÷",
"ø", "ù", "ú", "û", "ü", "ý", "þ", "ÿ"
};
auto num = static_cast<i32>(ctx->localr[1].num());
if (0<=num && num<128) {
@ -479,7 +484,7 @@ std::string md5(const std::string& src) {
std::vector<u32> buff;
usize num = ((src.length()+8)>>6)+1;
usize buffsize = num<<4;
buff.resize(buffsize,0);
buff.resize(buffsize, 0);
for(usize i = 0; i<src.length(); i++) {
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))
const u32 k[] = {
0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,0xa679438e,0x49b40821,
0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,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
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
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
const u32 s[] = {
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,
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
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,
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
};
// index
const u32 idx[] = {
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;
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, 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;
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;
};
#define shift(x,n) (((x)<<(n))|((x)>>(32-(n)))) // cycle left shift
#define md5f(x,y,z) (((x)&(y))|((~x)&(z)))
#define md5g(x,y,z) (((x)&(z))|((y)&(~z)))
#define md5h(x,y,z) ((x)^(y)^(z))
#define md5i(x,y,z) ((y)^((x)|(~z)))
#define shift(x, n) (((x)<<(n))|((x)>>(32-(n)))) // cycle left shift
#define md5f(x, y, z) (((x)&(y))|((~x)&(z)))
#define md5g(x, y, z) (((x)&(z))|((y)&(~z)))
#define md5h(x, y, z) ((x)^(y)^(z))
#define md5i(x, y, z) ((y)^((x)|(~z)))
u32 atmp = 0x67452301, btmp = 0xefcdab89;
u32 ctmp = 0x98badcfe, dtmp = 0x10325476;
@ -533,7 +546,7 @@ std::string md5(const std::string& src) {
u32 tmp = d;
d = c;
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;
}
atmp += a;
@ -565,13 +578,13 @@ public:
stamp = std::chrono::high_resolution_clock::now();
}
f64 elapsed_milliseconds() {
auto duration = std::chrono::high_resolution_clock::now() - stamp;
auto elapsed_milliseconds() const {
const auto duration = std::chrono::high_resolution_clock::now() - stamp;
return std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
}
f64 elapsed_microseconds() {
auto duration = std::chrono::high_resolution_clock::now() - stamp;
auto elapsed_microseconds() const {
const auto duration = std::chrono::high_resolution_clock::now() - stamp;
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);
}
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) {
@ -616,7 +629,7 @@ var builtin_elapsed_microsecond(context* ctx, gc* ngc) {
return var::num(-1);
}
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) {
@ -644,20 +657,27 @@ var builtin_gcextend(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);
double total = 0;
f64 total = 0;
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;
map["total"] = var::num(ngc->worktime*1.0/den*1000);
map["average"] = var::num(ngc->worktime*1.0/den*1000/total);
map["max_gc"] = var::num(ngc->max_time*1.0/den*1000);
map["max_mark"] = var::num(ngc->max_mark_time*1.0/den*1000);
map["max_sweep"] = var::num(ngc->max_sweep_time*1.0/den*1000);
const auto worktime = static_cast<f64>(ngc->worktime);
const auto max_time = static_cast<f64>(ngc->max_time);
const auto max_mark_time = static_cast<f64>(ngc->max_mark_time);
const auto max_sweep_time = static_cast<f64>(ngc->max_sweep_time);
// 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;
}
@ -682,9 +702,17 @@ var builtin_ghosttype(context* ctx, gc* ngc) {
if (!arg.is_ghost()) {
return nas_err("ghosttype", "this is not a ghost object.");
}
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()) {
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);
}

View File

@ -66,10 +66,12 @@ var builtin_values(context*, gc*);
var builtin_sleep(context*, gc*);
var builtin_platform(context*, gc*);
var builtin_arch(context*, gc*);
// md5 related functions
std::string tohex(u32);
std::string md5(const std::string&);
var builtin_md5(context*, gc*);
var builtin_maketimestamp(context*, gc*);
var builtin_time_stamp(context*, gc*);
var builtin_elapsed_millisecond(context*, gc*);
@ -80,7 +82,7 @@ var builtin_logtime(context*, gc*);
var builtin_ghosttype(context*, gc*);
// 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 {
const char* name;
var (*func)(context*, gc*);

View File

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

View File

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

View File

@ -17,7 +17,7 @@ var test_func = func(test_processes...) {
info = runtime.gc.info();
println("[", os.time(), "] ", duration, " ms, gc ",
(info.total-gc_total)*100/duration, "%, ",
int(1000/duration), " cps");
1000/duration, " cps");
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]) {
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 shutil
nasal_version = "11.1"
build_directory = pathlib.Path("build")
if not os.path.exists(build_directory):
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())
# create package directory in build directory and copy files needed
package_directory = build_directory.joinpath(tar_file_name)
if not os.path.exists(package_directory):
os.mkdir(package_directory)
os.mkdir(package_directory.joinpath("module"))
print("pack nasal executable")
shutil.copy(nasal_executable, package_directory.joinpath(nasal_executable))
print("pack nasal standard library")
shutil.copytree(nasal_standard_library, package_directory.joinpath(nasal_standard_library))
for m in nasal_modules:
print("pack nasal module:", m)
shutil.copy(m, package_directory.joinpath(m))
file = tarfile.open(name=tar_file_name + ".tar", mode="w")
file.add(package_directory)
print("pack succeeded")
file.close()