Merge pull request #33 from ValKmjolnir/develop

🐛 fix bug in debugger and symbol_finder & code improvement
This commit is contained in:
ValK 2023-10-27 00:16:01 +08:00 committed by GitHub
commit d6e1408edb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 2137 additions and 1637 deletions

View File

@ -36,6 +36,7 @@ set(NASAL_OBJECT_SOURCE_FILE
${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp
${CMAKE_SOURCE_DIR}/src/nasal_opcode.cpp
${CMAKE_SOURCE_DIR}/src/nasal_parse.cpp
${CMAKE_SOURCE_DIR}/src/nasal_type.cpp
${CMAKE_SOURCE_DIR}/src/nasal_vm.cpp
${CMAKE_SOURCE_DIR}/src/optimizer.cpp
${CMAKE_SOURCE_DIR}/src/symbol_finder.cpp
@ -65,6 +66,7 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/module)
set(MODULE_USED_OBJECT_SOURCE_FILE
${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp
${CMAKE_SOURCE_DIR}/src/nasal_type.cpp
${CMAKE_SOURCE_DIR}/src/nasal_gc.cpp)
add_library(module-used-object STATIC ${MODULE_USED_OBJECT_SOURCE_FILE})

View File

@ -4,7 +4,7 @@
![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)
![in dev](https://img.shields.io/badge/dev-v11.0-blue?style=flat-square&logo=github)
![in dev](https://img.shields.io/badge/dev-v11.1-blue?style=flat-square&logo=github)
[![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](./LICENSE)
> This document is also available in: [__中文__](./doc/README_zh.md) | [__English__](./README.md)

View File

@ -4,7 +4,7 @@
![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)
![in dev](https://img.shields.io/badge/dev-v11.0-blue?style=flat-square&logo=github)
![in dev](https://img.shields.io/badge/dev-v11.1-blue?style=flat-square&logo=github)
[![license](https://img.shields.io/badge/license-GPLv2-green?style=flat-square&logo=github)](../LICENSE)
> 这篇文档包含多语言版本: [__中文__](../doc/README_zh.md) | [__English__](../README.md)

View File

@ -1,11 +1,13 @@
STD = c++17
OS = $(shell uname)
ifndef OS
OS = $(shell uname)
endif
ifeq ($(OS), Darwin)
CXXFLAGS = -std=$(STD) -c -O3 -fno-exceptions -fPIC -mmacosx-version-min=10.15
else
CXXFLAGS = -std=$(STD) -c -O3 -fno-exceptions -fPIC
endif
CPPFLAGS = -I .
NASAL_HEADER=\
src/ast_dumper.h\
@ -21,6 +23,7 @@ NASAL_HEADER=\
src/nasal_opcode.h\
src/nasal_parse.h\
src/nasal_vm.h\
src/nasal_type.h\
src/nasal.h\
src/optimizer.h\
src/symbol_finder.h\
@ -55,6 +58,7 @@ NASAL_OBJECT=\
build/unix_lib.o\
build/dylib_lib.o\
build/coroutine.o\
build/nasal_type.o\
build/nasal_vm.o\
build/nasal_dbg.o\
build/repl.o\
@ -87,7 +91,10 @@ build/repl.o: $(NASAL_HEADER) src/repl.h src/repl.cpp | build
build/nasal_err.o: src/nasal.h src/repl.h src/nasal_err.h src/nasal_err.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_err.cpp -o build/nasal_err.o
build/nasal_gc.o: src/nasal.h src/nasal_gc.h src/nasal_gc.cpp | build
build/nasal_type.o: src/nasal.h src/nasal_type.h src/nasal_type.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_type.cpp -o build/nasal_type.o
build/nasal_gc.o: src/nasal.h src/nasal_type.h src/nasal_gc.h src/nasal_gc.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_gc.cpp -o build/nasal_gc.o
build/nasal_import.o: \
@ -113,18 +120,21 @@ build/nasal_ast.o: \
build/nasal_builtin.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/nasal_builtin.h src/nasal_builtin.cpp | build
$(CXX) $(CXXFLAGS) src/nasal_builtin.cpp -o build/nasal_builtin.o
build/coroutine.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/coroutine.h src/coroutine.cpp | build
$(CXX) $(CXXFLAGS) src/coroutine.cpp -o build/coroutine.o
build/bits_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/bits_lib.h src/bits_lib.cpp | build
$(CXX) $(CXXFLAGS) src/bits_lib.cpp -o build/bits_lib.o
@ -132,30 +142,35 @@ build/bits_lib.o: \
build/math_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/math_lib.h src/math_lib.cpp | build
$(CXX) $(CXXFLAGS) src/math_lib.cpp -o build/math_lib.o
build/io_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/io_lib.h src/io_lib.cpp | build
$(CXX) $(CXXFLAGS) src/io_lib.cpp -o build/io_lib.o
build/dylib_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/dylib_lib.h src/dylib_lib.cpp | build
$(CXX) $(CXXFLAGS) src/dylib_lib.cpp -o build/dylib_lib.o
build/unix_lib.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/unix_lib.h src/unix_lib.cpp | build
$(CXX) $(CXXFLAGS) src/unix_lib.cpp -o build/unix_lib.o
build/fg_props.o: \
src/nasal.h\
src/nasal_type.h\
src/nasal_gc.h\
src/fg_props.h src/fg_props.cpp | build
$(CXX) $(CXXFLAGS) src/fg_props.cpp -o build/fg_props.o

View File

@ -2,6 +2,8 @@
#include <iostream>
#include "../src/nasal.h"
#include "../src/nasal_type.h"
#include "../src/nasal_gc.h"
namespace nasal {
namespace fib_module {
@ -18,14 +20,14 @@ var fib(var* args, usize size, gc* ngc) {
return nas_err("fib", "lack arguments");
}
var num = args[0];
return var::num(fibonaci(num.tonum()));
return var::num(fibonaci(num.to_num()));
}
var quick_fib(var* args, usize size, gc* ngc) {
if (!size) {
return nas_err("quick_fib","lack arguments");
}
double num = args[0].tonum();
double num = args[0].to_num();
if (num<2) {
return var::num(num);
}
@ -51,30 +53,30 @@ void ghost_for_test_destructor(void* ptr) {
var create_new_ghost(var* args, usize size, gc* ngc) {
var res = ngc->alloc(vm_obj);
res.obj().set(ghost_for_test, ghost_for_test_destructor, new u32);
res.ghost().set(ghost_for_test, ghost_for_test_destructor, new u32);
return res;
}
var set_new_ghost(var* args, usize size, gc* ngc) {
var res = args[0];
if (!res.objchk(ghost_for_test)) {
if (!res.object_check(ghost_for_test)) {
std::cout << "set_new_ghost: not ghost for test type.\n";
return nil;
}
f64 num = args[1].num();
*((u32*)res.obj().ptr) = static_cast<u32>(num);
*((u32*)res.ghost().pointer) = static_cast<u32>(num);
std::cout << "set_new_ghost: successfully set ghost = " << num << "\n";
return nil;
}
var print_new_ghost(var* args, usize size, gc* ngc) {
var res = args[0];
if (!res.objchk(ghost_for_test)) {
if (!res.object_check(ghost_for_test)) {
std::cout << "print_new_ghost: not ghost for test type.\n";
return nil;
}
std::cout << "print_new_ghost: " << res.obj() << " result = "
<< *((u32*)res.obj().ptr) << "\n";
std::cout << "print_new_ghost: " << res.ghost() << " result = "
<< *((u32*)res.ghost().pointer) << "\n";
return nil;
}

View File

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

View File

@ -3,11 +3,14 @@
dynamic_libs_so = libfib.so libkey.so libnasock.so libmat.so
dynamic_libs_dll = libfib.dll libkey.dll libnasock.dll libmat.dll
used_header = ../src/nasal.h ../src/nasal_gc.h
used_object = ../build/nasal_misc.o ../build/nasal_gc.o
used_header = ../src/nasal.h ../src/nasal_type.h ../src/nasal_gc.h
used_object = ../build/nasal_misc.o ../build/nasal_type.o ../build/nasal_gc.o
STD = c++17
OS = $(shell uname)
ifndef OS
OS = $(shell uname)
endif
ifeq ($(OS), Darwin)
CXXFLAGS = -std=$(STD) -c -O3 -fPIC -mmacosx-version-min=10.15
else

View File

@ -1,4 +1,6 @@
#include "../src/nasal.h"
#include "../src/nasal_type.h"
#include "../src/nasal_gc.h"
#include <cmath>
namespace nasal {

View File

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

View File

@ -386,7 +386,11 @@ bool ast_dumper::visit_for_expr(for_expr* node) {
bool ast_dumper::visit_iter_expr(iter_expr* node) {
dump_indent();
std::cout << "iterator";
if (node->is_definition()) {
std::cout << "iterator_definition";
} else {
std::cout << "iterator";
}
std::cout << format_location(node->get_location());
push_indent();
set_last();

View File

@ -2,55 +2,62 @@
namespace nasal {
var builtin_u32xor(var* local, gc& ngc) {
var builtin_u32xor(context* ctx, gc* ngc) {
auto local = ctx->localr;
return var::num(static_cast<f64>(
static_cast<u32>(local[1].num()) ^
static_cast<u32>(local[2].num())
));
}
var builtin_u32and(var* local, gc& ngc) {
var builtin_u32and(context* ctx, gc* ngc) {
auto local = ctx->localr;
return var::num(static_cast<f64>(
static_cast<u32>(local[1].num()) &
static_cast<u32>(local[2].num())
));
}
var builtin_u32or(var* local, gc& ngc) {
var builtin_u32or(context* ctx, gc* ngc) {
auto local = ctx->localr;
return var::num(static_cast<f64>(
static_cast<u32>(local[1].num()) |
static_cast<u32>(local[2].num())
));
}
var builtin_u32nand(var* local, gc& ngc) {
var builtin_u32nand(context* ctx, gc* ngc) {
auto local = ctx->localr;
return var::num(static_cast<f64>(~(
static_cast<u32>(local[1].num()) &
static_cast<u32>(local[2].num())
)));
}
var builtin_u32not(var* local, gc& ngc) {
return var::num(static_cast<f64>(~static_cast<u32>(local[1].num())));
var builtin_u32not(context* ctx, gc* ngc) {
return var::num(static_cast<f64>(
~static_cast<u32>(ctx->localr[1].num())
));
}
var builtin_fld(var* local, gc& ngc) {
var builtin_fld(context* ctx, gc* ngc) {
// bits.fld(s,0,3);
// if s stores 10100010(162)
// will get 101(5)
var str = local[1];
var startbit = local[2];
var length = local[3];
if (str.type!=vm_str || str.val.gcobj->unmut) {
return nas_err("fld", "\"str\" must be mutable string");
auto local = ctx->localr;
auto str = local[1];
auto startbit = local[2];
auto length = local[3];
if (str.type!=vm_str || str.val.gcobj->unmutable) {
return nas_err("bits::fld", "\"str\" must be mutable string");
}
if (startbit.type!=vm_num || length.type!=vm_num) {
return nas_err("fld", "\"startbit\",\"len\" must be number");
return nas_err("bits::fld", "\"startbit\",\"len\" must be number");
}
u32 bit = static_cast<u32>(startbit.num());
u32 len = static_cast<u32>(length.num());
if (bit+len>8*str.str().length()) {
return nas_err("fld", "bitfield out of bounds");
return nas_err("bits::fld", "bitfield out of bounds");
}
u32 res = 0;
auto& s = str.str();
@ -62,24 +69,25 @@ var builtin_fld(var* local, gc& ngc) {
return var::num(static_cast<f64>(res));
}
var builtin_sfld(var* local, gc& ngc) {
var builtin_sfld(context* ctx, gc* ngc) {
// bits.sfld(s,0,3);
// if s stores 10100010(162)
// will get 101(5) then this will be signed extended to
// 11111101(-3)
var str = local[1];
var startbit = local[2];
var length = local[3];
if (str.type!=vm_str || str.val.gcobj->unmut) {
return nas_err("sfld", "\"str\" must be mutable string");
auto local = ctx->localr;
auto str = local[1];
auto startbit = local[2];
auto length = local[3];
if (str.type!=vm_str || str.val.gcobj->unmutable) {
return nas_err("bits::sfld", "\"str\" must be mutable string");
}
if (startbit.type!=vm_num || length.type!=vm_num) {
return nas_err("sfld", "\"startbit\",\"len\" must be number");
return nas_err("bits::sfld", "\"startbit\",\"len\" must be number");
}
u32 bit = static_cast<u32>(startbit.num());
u32 len = static_cast<u32>(length.num());
if (bit+len>8*str.str().length()) {
return nas_err("sfld", "bitfield out of bounds");
return nas_err("bits::sfld", "bitfield out of bounds");
}
u32 res = 0;
auto& s = str.str();
@ -94,26 +102,29 @@ var builtin_sfld(var* local, gc& ngc) {
return var::num(static_cast<f64>(static_cast<i32>(res)));
}
var builtin_setfld(var* local, gc& ngc) {
var builtin_setfld(context* ctx, gc* ngc) {
// bits.setfld(s,0,8,69);
// set 01000101(69) to string will get this:
// 10100010(162)
// so s[0]=162
var str = local[1];
var startbit = local[2];
var length = local[3];
var value = local[4];
if (str.type!=vm_str || str.val.gcobj->unmut) {
return nas_err("setfld", "\"str\" must be mutable string");
auto local = ctx->localr;
auto str = local[1];
auto startbit = local[2];
auto length = local[3];
auto value = local[4];
if (str.type!=vm_str || str.val.gcobj->unmutable) {
return nas_err("bits::setfld", "\"str\" must be mutable string");
}
if (startbit.type!=vm_num || length.type!=vm_num || value.type!=vm_num) {
return nas_err("setfld", "\"startbit\",\"len\",\"val\" must be number");
return nas_err("bits::setfld",
"\"startbit\", \"len\", \"val\" must be number"
);
}
u32 bit = static_cast<u32>(startbit.num());
u32 len = static_cast<u32>(length.num());
u64 val = static_cast<u64>(value.num());
if (bit+len>8*str.str().length()) {
return nas_err("setfld", "bitfield out of bounds");
return nas_err("bits::setfld", "bitfield out of bounds");
}
auto& s = str.str();
for(u32 i = bit; i<bit+len; ++i) {
@ -126,12 +137,12 @@ var builtin_setfld(var* local, gc& ngc) {
return nil;
}
var builtin_buf(var* local, gc& ngc) {
var length = local[1];
var builtin_buf(context* ctx, gc* ngc) {
var length = ctx->localr[1];
if (length.type!=vm_num || length.num()<=0) {
return nas_err("buf", "\"len\" must be number greater than 0");
return nas_err("bits::buf", "\"len\" must be number greater than 0");
}
var str = ngc.alloc(vm_str);
var str = ngc->alloc(vm_str);
auto& s = str.str();
s.resize(length.num(), '\0');
return str;

View File

@ -6,15 +6,15 @@
namespace nasal {
var builtin_u32xor(var*, gc&);
var builtin_u32and(var*, gc&);
var builtin_u32or(var*, gc&);
var builtin_u32nand(var*, gc&);
var builtin_u32not(var*, gc&);
var builtin_fld(var*, gc&);
var builtin_sfld(var*, gc&);
var builtin_setfld(var*, gc&);
var builtin_buf(var*, gc&);
var builtin_u32xor(context*, gc*);
var builtin_u32and(context*, gc*);
var builtin_u32or(context*, gc*);
var builtin_u32nand(context*, gc*);
var builtin_u32not(context*, gc*);
var builtin_fld(context*, gc*);
var builtin_sfld(context*, gc*);
var builtin_setfld(context*, gc*);
var builtin_buf(context*, gc*);
extern nasal_builtin_table bits_native[];

View File

@ -2,7 +2,8 @@
namespace nasal {
var builtin_cocreate(var* local, gc& ngc) {
var builtin_cocreate(context* ctx, gc* ngc) {
// ```
// +-------------+
// | old pc | <- top[0]
// +-------------+
@ -15,55 +16,73 @@ var builtin_cocreate(var* local, gc& ngc) {
// +-------------+ <- local pointer stored in localr
// | old funcr | <- old function stored in funcr
// +-------------+
var func = local[1];
if (func.type!=vm_func) {
return nas_err("coroutine::create", "must use a function to create coroutine");
// ```
auto coroutine_function = ctx->localr[1];
if (coroutine_function.type!=vm_func) {
return nas_err(
"coroutine::create",
"must use a function to create coroutine"
);
}
if (ngc.cort) {
return nas_err("coroutine::create", "cannot create another coroutine in a coroutine");
if (ngc->cort) {
return nas_err(
"coroutine::create",
"cannot create another coroutine in a coroutine"
);
}
var co = ngc.alloc(vm_co);
nas_co& cort = co.co();
cort.ctx.pc = func.func().entry-1;
auto coroutine_object = ngc->alloc(vm_co);
auto& coroutine = coroutine_object.co();
coroutine.ctx.pc = coroutine_function.func().entry-1;
cort.ctx.top[0] = nil;
cort.ctx.localr = cort.ctx.top+1;
cort.ctx.top = cort.ctx.localr+func.func().lsize;
cort.ctx.localr[0] = func.func().local[0];
cort.ctx.top[0] = nil; // old upvalr
cort.ctx.top++;
cort.ctx.top[0] = var::addr((var*)nullptr); // old localr
cort.ctx.top++;
cort.ctx.top[0] = var::ret(0); // old pc, set to zero to make op_ret recognizing this as coroutine function
coroutine.ctx.top[0] = nil;
coroutine.ctx.localr = coroutine.ctx.top+1;
coroutine.ctx.top = coroutine.ctx.localr +
coroutine_function.func().local_size;
coroutine.ctx.localr[0] = coroutine_function.func().local[0];
cort.ctx.funcr = func; // make sure the coroutine function can use correct upvalues
cort.status = nas_co::status::suspended;
return co;
// store old upvalr on stack
coroutine.ctx.top[0] = nil;
coroutine.ctx.top++;
// store old localr on stack
coroutine.ctx.top[0] = var::addr((var*)nullptr);
coroutine.ctx.top++;
// store old pc on stack
// set to zero to make op_ret recognizing this as coroutine function
coroutine.ctx.top[0] = var::ret(0);
// make sure the coroutine function can use correct upvalues
coroutine.ctx.funcr = coroutine_function;
coroutine.status = nas_co::status::suspended;
return coroutine_object;
}
var builtin_coresume(var* local, gc& ngc) {
if (ngc.cort) {
return nas_err("coroutine::resume", "cannot start another coroutine when one is running");
var builtin_coresume(context* ctx, gc* ngc) {
if (ngc->cort) {
return nas_err(
"coroutine::resume",
"cannot start another coroutine when one is running"
);
}
var co = local[1];
// return nil if is not a coroutine object
if (co.type!=vm_co) {
return nil;
}
// cannot resume a dead coroutine
if (co.co().status==nas_co::status::dead) {
auto main_local_frame = ctx->localr;
auto coroutine_object = main_local_frame[1];
// return nil if is not a coroutine object or coroutine exited
if (coroutine_object.type!=vm_co ||
coroutine_object.co().status==nas_co::status::dead) {
return nil;
}
// change to coroutine context
ngc.ctxchg(co.co());
ngc->context_change(&coroutine_object.co());
// fetch coroutine's stack top and return
// then coroutine's stack top will catch this return value
// so the coroutine's stack top in fact is not changed
if (ngc.rctx->top[0].type==vm_ret) {
if (ngc->running_context->top[0].type==vm_ret) {
// when first calling this coroutine, the stack top must be vm_ret
return ngc.rctx->top[0];
return ngc->running_context->top[0];
}
// after first calling the coroutine, each time coroutine.yield triggered
@ -73,39 +92,41 @@ var builtin_coresume(var* local, gc& ngc) {
// the coroutine seems like coroutine.yield returns the value
// but in fact coroutine.yield stop the coroutine
// until main context calls the coroutine.resume
return local[2];
return main_local_frame[2];
}
var builtin_coyield(var* local, gc& ngc) {
if (!ngc.cort) {
var builtin_coyield(context* ctx, gc* ngc) {
if (!ngc->cort) {
return nas_err("coroutine::yield", "no coroutine is running");
}
// get coroutine local frame
auto coroutine_local_frame = ctx->localr;
// vm context will set to main context
ngc->context_reserve();
// this will set to main stack top
ngc.ctxreserve();
// then this will return value to main's stack top[0]
// the procedure seems like coroutine.resume returns the value
// but in fact coroutine.resume stop the main context
// until coroutine calls the coroutine.yield
return local[1];
return coroutine_local_frame[1];
}
var builtin_costatus(var* local, gc& ngc) {
var co = local[1];
if (co.type!=vm_co) {
return ngc.newstr("error");
var builtin_costatus(context* ctx, gc* ngc) {
auto coroutine_object = ctx->localr[1];
if (coroutine_object.type!=vm_co) {
return ngc->newstr("error");
}
switch(co.co().status) {
case nas_co::status::suspended: return ngc.newstr("suspended");
case nas_co::status::running: return ngc.newstr("running");
case nas_co::status::dead: return ngc.newstr("dead");
switch(coroutine_object.co().status) {
case nas_co::status::suspended: return ngc->newstr("suspended");
case nas_co::status::running: return ngc->newstr("running");
case nas_co::status::dead: return ngc->newstr("dead");
}
return nil;
}
var builtin_corun(var* local, gc& ngc) {
return ngc.cort? one:zero;
var builtin_corun(context* ctx, gc* ngc) {
return ngc->cort? one:zero;
}
nasal_builtin_table coroutine_native[] = {

View File

@ -6,11 +6,11 @@
namespace nasal {
var builtin_cocreate(var*, gc&);
var builtin_coresume(var*, gc&);
var builtin_coyield(var*, gc&);
var builtin_costatus(var*, gc&);
var builtin_corun(var*, gc&);
var builtin_cocreate(context*, gc*);
var builtin_coresume(context*, gc*);
var builtin_coyield(context*, gc*);
var builtin_costatus(context*, gc*);
var builtin_corun(context*, gc*);
extern nasal_builtin_table coroutine_native[];

View File

@ -2,107 +2,130 @@
namespace nasal {
const auto dylib_type_name = "dylib";
const auto func_addr_type_name = "faddr";
const auto dynamic_library_type_name = "dylib";
const auto function_address_type_name = "faddr";
void dylib_destructor(void* ptr) {
void dynamic_library_destructor(void* pointer) {
#ifdef _WIN32
FreeLibrary(static_cast<HMODULE>(ptr));
FreeLibrary(static_cast<HMODULE>(pointer));
#else
dlclose(ptr);
dlclose(pointer);
#endif
}
void func_addr_destructor(void* ptr) {}
var builtin_dlopen(var* local, gc& ngc) {
var dlname = local[1];
var builtin_dlopen(context* ctx, gc* ngc) {
auto dlname = ctx->localr[1];
if (dlname.type!=vm_str) {
return nas_err("dlopen", "\"libname\" must be string");
return nas_err("dylib::dlopen", "\"libname\" must be string");
}
#ifdef _WIN32
wchar_t* str = new wchar_t[dlname.str().size()+1];
if (!str) {
return nas_err("dlopen", "malloc failed");
}
memset(str, 0, sizeof(wchar_t)*dlname.str().size()+1);
mbstowcs(str, dlname.str().c_str(),dlname.str().size()+1);
void* ptr = LoadLibraryA(dlname.str().c_str());
delete []str;
#else
void* ptr = dlopen(dlname.str().c_str(), RTLD_LOCAL|RTLD_LAZY);
#endif
if (!ptr) {
return nas_err("dlopen", "cannot open dynamic lib <"+dlname.str()+">");
}
var ret = ngc.temp = ngc.alloc(vm_hash);
var lib = ngc.alloc(vm_obj);
lib.obj().set(dylib_type_name, dylib_destructor, ptr);
ret.hash().elems["lib"] = lib;
// get library pointer
#ifdef _WIN32
void* func = (void*)GetProcAddress(
static_cast<HMODULE>(lib.obj().ptr),
"get"
wchar_t* wide_string = new wchar_t[dlname.str().size()+1];
if (!wide_string) {
return nas_err("dylib::dlopen", "malloc failed");
}
memset(wide_string, 0, sizeof(wchar_t) * dlname.str().size() + 1);
mbstowcs(wide_string, dlname.str().c_str(), dlname.str().size() + 1);
// load library by using wide string name
void* dynamic_library_pointer = LoadLibraryA(dlname.str().c_str());
delete []wide_string;
#else
void* dynamic_library_pointer = dlopen(
dlname.str().c_str(), RTLD_LOCAL|RTLD_LAZY
);
#endif
// check library pointer and insert into returned hashmap
if (!dynamic_library_pointer) {
return nas_err("dylib::dlopen",
"cannot open dynamic lib <" + dlname.str() + ">"
);
}
auto return_hash = ngc->temp = ngc->alloc(vm_hash);
auto library_object = ngc->alloc(vm_obj);
library_object.ghost().set(
dynamic_library_type_name,
dynamic_library_destructor,
dynamic_library_pointer
);
return_hash.hash().elems["lib"] = library_object;
// get "get" function, to get the register table
#ifdef _WIN32
void* register_table_get_function = (void*)GetProcAddress(
static_cast<HMODULE>(library_object.ghost().pointer), "get"
);
#else
void* func = dlsym(lib.obj().ptr, "get");
void* register_table_get_function = dlsym(
library_object.ghost().pointer, "get"
);
#endif
if (!func) {
return nas_err("dlopen", "cannot find <get> function");
}
// get function pointer by name
module_func_info* tbl = reinterpret_cast<get_func_ptr>(func)();
if (!tbl) {
return nas_err("dlopen", "failed to get module functions");
}
for(u32 i = 0; tbl[i].name; ++i) {
void* p = (void*)tbl[i].fd;
var tmp = ngc.alloc(vm_obj);
tmp.obj().set(func_addr_type_name, func_addr_destructor, p);
ret.hash().elems[tbl[i].name] = tmp;
if (!register_table_get_function) {
return nas_err("dylib::dlopen", "cannot find <get> function");
}
ngc.temp = nil;
return ret;
// get function pointer by name
auto table = reinterpret_cast<get_func_ptr>(register_table_get_function)();
if (!table) {
return nas_err("dylib::dlopen", "failed to get module functions");
}
for(u32 i = 0; table[i].name; ++i) {
auto function_pointer = reinterpret_cast<void*>(table[i].fd);
auto function_object = ngc->alloc(vm_obj);
function_object.ghost().set(
function_address_type_name,
nullptr,
function_pointer
);
return_hash.hash().elems[table[i].name] = function_object;
}
ngc->temp = nil;
return return_hash;
}
var builtin_dlclose(var* local, gc& ngc) {
var libptr = local[1];
if (!libptr.objchk(dylib_type_name)) {
return nas_err("dlclose", "\"lib\" is not a valid dynamic lib");
var builtin_dlclose(context* ctx, gc* ngc) {
auto library_pointer = ctx->localr[1];
if (!library_pointer.object_check(dynamic_library_type_name)) {
return nas_err("dylib::dlclose", "\"lib\" is not a valid dynamic lib");
}
libptr.obj().clear();
library_pointer.ghost().clear();
return nil;
}
var builtin_dlcallv(var* local, gc& ngc) {
var fp = local[1];
var args = local[2];
if (!fp.objchk(func_addr_type_name)) {
return nas_err("dlcall", "\"ptr\" is not a valid function pointer");
var builtin_dlcallv(context* ctx, gc* ngc) {
auto function_object = ctx->localr[1];
auto arguments = ctx->localr[2];
if (!function_object.object_check(function_address_type_name)) {
return nas_err("dylib::dlcall",
"\"ptr\" is not a valid function pointer"
);
}
auto& vec = args.vec().elems;
return reinterpret_cast<module_func>(fp.obj().ptr)(
auto& vec = arguments.vec().elems;
return reinterpret_cast<module_func>(function_object.ghost().pointer)(
vec.data(),
vec.size(),
&ngc
ngc
);
}
var builtin_dlcall(var* local, gc& ngc) {
var fp = local[1];
if (!fp.objchk(func_addr_type_name)) {
return nas_err("dlcall", "\"ptr\" is not a valid function pointer");
var builtin_dlcall(context* ctx, gc* ngc) {
auto function_object = ctx->localr[1];
if (!function_object.object_check(function_address_type_name)) {
return nas_err("dylib::dlcall",
"\"ptr\" is not a valid function pointer"
);
}
var* local_frame_start = local+2;
usize local_frame_size = ngc.rctx->top-local_frame_start;
// arguments' stored place begins at local +2
return reinterpret_cast<module_func>(fp.obj().ptr)(
// function pointer is at ctx->localr[1]
// so arguments starts from ctx->localr[2]
var* local_frame_start = ctx->localr + 2;
usize local_frame_size = ngc->running_context->top - local_frame_start;
return reinterpret_cast<module_func>(function_object.ghost().pointer)(
local_frame_start,
local_frame_size,
&ngc
ngc
);
}

View File

@ -13,13 +13,12 @@
namespace nasal {
void dylib_destructor(void*);
void func_addr_destructor(void*);
void dynamic_library_destructor(void*);
var builtin_dlopen(var*, gc&);
var builtin_dlclose(var*, gc&);
var builtin_dlcallv(var*, gc&);
var builtin_dlcall(var*, gc&);
var builtin_dlopen(context*, gc*);
var builtin_dlclose(context*, gc*);
var builtin_dlcallv(context*, gc*);
var builtin_dlcall(context*, gc*);
extern nasal_builtin_table dylib_lib_native[];

View File

@ -4,11 +4,12 @@
namespace nasal {
var builtin_logprint(var* local, gc& ngc) {
var level = local[1];
var elems = local[2];
var builtin_logprint(context* ctx, gc* ngc) {
auto local = ctx->localr;
auto level = local[1];
auto elems = local[2];
if (elems.type!=vm_vec) {
return nas_err("logprint", "received argument is not vector.");
return nas_err("fg_env::logprint", "received argument is not vector.");
}
std::ofstream out("fgfs.log", std::ios::app);
switch (static_cast<u32>(level.num())) {
@ -21,13 +22,12 @@ var builtin_logprint(var* local, gc& ngc) {
case SG_DEV_ALERT: out << "[DEV_ALERT]"; break;
case SG_MANDATORY_INFO: out << "[MANDATORY_INFO]"; break;
default:
return nas_err("logprint",
"incorrect log level " +
std::to_string(level.num())
return nas_err("fg_env::logprint",
"incorrect log level " + std::to_string(level.num())
);
}
for(auto& i : elems.vec().elems) {
out << i << " ";
for(auto& value : elems.vec().elems) {
out << value << " ";
}
out << "\n";
return nil;

View File

@ -15,7 +15,7 @@ namespace nasal {
#define SG_DEV_ALERT 8
#define SG_MANDATORY_INFO 9
var builtin_logprint(var*, gc&);
var builtin_logprint(context*, gc*);
extern nasal_builtin_table flight_gear_native[];

View File

@ -11,187 +11,200 @@ void filehandle_destructor(void* ptr) {
fclose(static_cast<FILE*>(ptr));
}
var builtin_readfile(var* local, gc& ngc) {
var val = local[1];
if (val.type!=vm_str) {
var builtin_readfile(context* ctx, gc* ngc) {
auto filename = ctx->localr[1];
if (filename.type!=vm_str) {
return nas_err("io::readfile", "\"filename\" must be string");
}
std::ifstream in(val.str(), std::ios::binary);
std::ifstream in(filename.str(), std::ios::binary);
std::stringstream rd;
if (!in.fail()) {
rd << in.rdbuf();
}
return ngc.newstr(rd.str());
return ngc->newstr(rd.str());
}
var builtin_fout(var* local, gc& ngc) {
var val = local[1];
var str = local[2];
if (val.type!=vm_str) {
var builtin_fout(context* ctx, gc* ngc) {
auto local = ctx->localr;
auto filename = local[1];
auto source = local[2];
if (filename.type!=vm_str) {
return nas_err("io::fout", "\"filename\" must be string");
}
std::ofstream out(val.str());
std::ofstream out(filename.str());
if (out.fail()) {
return nas_err("io::fout", "cannot open <"+val.str()+">");
return nas_err("io::fout", "cannot open <" + filename.str() + ">");
}
out << str;
out << source;
return nil;
}
var builtin_exists(var* local, gc& ngc) {
if (local[1].type!=vm_str) {
var builtin_exists(context* ctx, gc* ngc) {
auto filename = ctx->localr[1];
if (filename.type!=vm_str) {
return zero;
}
return access(local[1].str().c_str(), F_OK)!=-1?one:zero;
return access(filename.str().c_str(), F_OK)!=-1? one:zero;
}
var builtin_open(var* local, gc& ngc) {
var name = local[1];
var mode = local[2];
var builtin_open(context* ctx, gc* ngc) {
auto local = ctx->localr;
auto name = local[1];
auto mode = local[2];
if (name.type!=vm_str) {
return nas_err("open", "\"filename\" must be string");
return nas_err("io::open", "\"filename\" must be string");
}
if (mode.type!=vm_str) {
return nas_err("open", "\"mode\" must be string");
return nas_err("io::open", "\"mode\" must be string");
}
FILE* res = fopen(name.str().c_str(), mode.str().c_str());
if (!res) {
return nas_err("open", "failed to open file <"+name.str()+">");
auto file_descriptor = fopen(name.str().c_str(), mode.str().c_str());
if (!file_descriptor) {
return nas_err("io::open", "failed to open file <" + name.str() + ">");
}
var ret = ngc.alloc(vm_obj);
ret.obj().set(file_type_name, filehandle_destructor, res);
return ret;
var return_object = ngc->alloc(vm_obj);
return_object.ghost().set(
file_type_name, filehandle_destructor, file_descriptor
);
return return_object;
}
var builtin_close(var* local, gc& ngc) {
var fd = local[1];
if (!fd.objchk(file_type_name)) {
return nas_err("close", "not a valid filehandle");
var builtin_close(context* ctx, gc* ngc) {
var file_descriptor = ctx->localr[1];
if (!file_descriptor.object_check(file_type_name)) {
return nas_err("io::close", "not a valid filehandle");
}
fd.obj().clear();
file_descriptor.ghost().clear();
return nil;
}
var builtin_read(var* local, gc& ngc) {
var fd = local[1];
var buf = local[2];
var len = local[3];
if (!fd.objchk(file_type_name)) {
return nas_err("read", "not a valid filehandle");
var builtin_read(context* ctx, gc* ngc) {
auto local = ctx->localr;
auto file_descriptor = local[1];
auto buffer = local[2];
auto length = local[3];
if (!file_descriptor.object_check(file_type_name)) {
return nas_err("io::read", "not a valid filehandle");
}
if (buf.type!=vm_str || buf.val.gcobj->unmut) {
return nas_err("read", "\"buf\" must be mutable string");
if (buffer.type!=vm_str || buffer.val.gcobj->unmutable) {
return nas_err("io::read", "\"buf\" must be mutable string");
}
if (len.type!=vm_num) {
return nas_err("read", "\"len\" must be number");
if (length.type!=vm_num) {
return nas_err("io::read", "\"len\" must be number");
}
if (len.num()<=0 || len.num()>=(1<<30)) {
return nas_err("read", "\"len\" less than 1 or too large");
if (length.num()<=0 || length.num()>=(1<<30)) {
return nas_err("io::read", "\"len\" less than 1 or too large");
}
char* buff = new char[(usize)len.num()+1];
if (!buff) {
return nas_err("read", "malloc failed");
auto temp_buffer = new char[static_cast<usize>(length.num())+1];
if (!temp_buffer) {
return nas_err("io::read", "malloc failed");
}
f64 res = fread(buff, 1, len.num(), static_cast<FILE*>(fd.obj().ptr));
buf.str() = buff;
buf.val.gcobj->unmut = true;
delete []buff;
return var::num(res);
auto read_size = fread(
temp_buffer, 1, length.num(),
static_cast<FILE*>(file_descriptor.ghost().pointer)
);
buffer.str() = temp_buffer;
buffer.val.gcobj->unmutable = true;
delete []temp_buffer;
return var::num(read_size);
}
var builtin_write(var* local, gc& ngc) {
var fd = local[1];
var str = local[2];
if (!fd.objchk(file_type_name)) {
return nas_err("write", "not a valid filehandle");
var builtin_write(context* ctx, gc* ngc) {
auto local = ctx->localr;
auto file_descriptor = local[1];
auto source = local[2];
if (!file_descriptor.object_check(file_type_name)) {
return nas_err("io::write", "not a valid filehandle");
}
if (str.type!=vm_str) {
return nas_err("write", "\"str\" must be string");
if (source.type!=vm_str) {
return nas_err("io::write", "\"str\" must be string");
}
return var::num(static_cast<f64>(fwrite(
str.str().c_str(),
1,
str.str().length(),
static_cast<FILE*>(fd.obj().ptr)
source.str().c_str(), 1, source.str().length(),
static_cast<FILE*>(file_descriptor.ghost().pointer)
)));
}
var builtin_seek(var* local, gc& ngc) {
var fd = local[1];
var pos = local[2];
var whence = local[3];
if (!fd.objchk(file_type_name)) {
return nas_err("seek", "not a valid filehandle");
var builtin_seek(context* ctx, gc* ngc) {
auto local = ctx->localr;
auto file_descriptor = local[1];
auto position = local[2];
auto whence = local[3];
if (!file_descriptor.object_check(file_type_name)) {
return nas_err("io::seek", "not a valid filehandle");
}
return var::num(static_cast<f64>(fseek(
static_cast<FILE*>(fd.obj().ptr),
pos.num(),
static_cast<FILE*>(file_descriptor.ghost().pointer),
position.num(),
whence.num()
)));
}
var builtin_tell(var* local, gc& ngc) {
var fd = local[1];
if (!fd.objchk(file_type_name)) {
return nas_err("tell", "not a valid filehandle");
var builtin_tell(context* ctx, gc* ngc) {
auto file_descriptor = ctx->localr[1];
if (!file_descriptor.object_check(file_type_name)) {
return nas_err("io::tell", "not a valid filehandle");
}
return var::num(static_cast<f64>(ftell(static_cast<FILE*>(fd.obj().ptr))));
return var::num(static_cast<f64>(
ftell(static_cast<FILE*>(file_descriptor.ghost().pointer))
));
}
var builtin_readln(var* local, gc& ngc) {
var fd = local[1];
if (!fd.objchk(file_type_name)) {
return nas_err("readln", "not a valid filehandle");
var builtin_readln(context* ctx, gc* ngc) {
auto file_descriptor = ctx->localr[1];
if (!file_descriptor.object_check(file_type_name)) {
return nas_err("io::readln", "not a valid filehandle");
}
var str = ngc.alloc(vm_str);
auto result = ngc->alloc(vm_str);
char c;
while((c = fgetc(static_cast<FILE*>(fd.obj().ptr)))!=EOF) {
while((c = fgetc(static_cast<FILE*>(file_descriptor.ghost().pointer)))!=EOF) {
if (c=='\r') {
continue;
}
if (c=='\n') {
return str;
return result;
}
str.str() += c;
result.str().push_back(c);
}
if (str.str().length()) {
return str;
if (result.str().length()) {
return result;
}
return nil;
}
var builtin_stat(var* local, gc& ngc) {
var name = local[1];
var builtin_stat(context* ctx, gc* ngc) {
auto name = ctx->localr[1];
if (name.type!=vm_str) {
return nas_err("stat", "\"filename\" must be string");
return nas_err("io::stat", "\"filename\" must be string");
}
struct stat buf;
if (stat(name.str().c_str(),&buf)<0) {
return nas_err("stat", "failed to open file <"+name.str()+">");
struct stat buffer;
if (stat(name.str().c_str(), &buffer)<0) {
return nas_err("io::stat", "failed to open file <" + name.str() + ">");
}
var ret = ngc.alloc(vm_vec);
ret.vec().elems = {
var::num(static_cast<f64>(buf.st_dev)),
var::num(static_cast<f64>(buf.st_ino)),
var::num(static_cast<f64>(buf.st_mode)),
var::num(static_cast<f64>(buf.st_nlink)),
var::num(static_cast<f64>(buf.st_uid)),
var::num(static_cast<f64>(buf.st_gid)),
var::num(static_cast<f64>(buf.st_rdev)),
var::num(static_cast<f64>(buf.st_size)),
var::num(static_cast<f64>(buf.st_atime)),
var::num(static_cast<f64>(buf.st_mtime)),
var::num(static_cast<f64>(buf.st_ctime))
auto result = ngc->alloc(vm_vec);
result.vec().elems = {
var::num(static_cast<f64>(buffer.st_dev)),
var::num(static_cast<f64>(buffer.st_ino)),
var::num(static_cast<f64>(buffer.st_mode)),
var::num(static_cast<f64>(buffer.st_nlink)),
var::num(static_cast<f64>(buffer.st_uid)),
var::num(static_cast<f64>(buffer.st_gid)),
var::num(static_cast<f64>(buffer.st_rdev)),
var::num(static_cast<f64>(buffer.st_size)),
var::num(static_cast<f64>(buffer.st_atime)),
var::num(static_cast<f64>(buffer.st_mtime)),
var::num(static_cast<f64>(buffer.st_ctime))
};
return ret;
return result;
}
var builtin_eof(var* local, gc& ngc) {
var fd = local[1];
if (!fd.objchk(file_type_name)) {
return nas_err("readln", "not a valid filehandle");
var builtin_eof(context* ctx, gc* ngc) {
auto file_descriptor = ctx->localr[1];
if (!file_descriptor.object_check(file_type_name)) {
return nas_err("io::readln", "not a valid filehandle");
}
return var::num(static_cast<f64>(feof(static_cast<FILE*>(fd.obj().ptr))));
return var::num(static_cast<f64>(
feof(static_cast<FILE*>(file_descriptor.ghost().pointer))
));
}
nasal_builtin_table io_lib_native[] = {

View File

@ -6,6 +6,12 @@
#include <sys/stat.h>
#ifndef _MSC_VER
#include <unistd.h>
#else
#include <io.h>
#endif
#ifdef _MSC_VER
#define F_OK 0 // fuck msc
#endif
@ -14,18 +20,18 @@ namespace nasal {
void filehandle_destructor(void*);
var builtin_readfile(var*, gc&);
var builtin_fout(var*, gc&);
var builtin_exists(var*, gc&);
var builtin_open(var*, gc&);
var builtin_close(var*, gc&);
var builtin_read(var*, gc&);
var builtin_write(var*, gc&);
var builtin_seek(var*, gc&);
var builtin_tell(var*, gc&);
var builtin_readln(var*, gc&);
var builtin_stat(var*, gc&);
var builtin_eof(var*, gc&);
var builtin_readfile(context*, gc*);
var builtin_fout(context*, gc*);
var builtin_exists(context*, gc*);
var builtin_open(context*, gc*);
var builtin_close(context*, gc*);
var builtin_read(context*, gc*);
var builtin_write(context*, gc*);
var builtin_seek(context*, gc*);
var builtin_tell(context*, gc*);
var builtin_readln(context*, gc*);
var builtin_stat(context*, gc*);
var builtin_eof(context*, gc*);
extern nasal_builtin_table io_lib_native[];

View File

@ -1,4 +1,6 @@
#include "nasal.h"
#include "nasal_type.h"
#include "nasal_gc.h"
#include "nasal_err.h"
#include "nasal_lexer.h"
#include "nasal_ast.h"

View File

@ -2,62 +2,62 @@
namespace nasal {
var builtin_pow(var* local, gc& ngc) {
var x = local[1];
var y = local[2];
var builtin_pow(context* ctx, gc* ngc) {
auto x = ctx->localr[1];
auto y = ctx->localr[2];
if (x.type!=vm_num || y.type!=vm_num) {
return var::num(std::nan(""));
}
return var::num(std::pow(x.num(), y.num()));
}
var builtin_sin(var* local, gc& ngc) {
var val = local[1];
var builtin_sin(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? sin(val.num()):std::nan(""));
}
var builtin_cos(var* local, gc& ngc) {
var val = local[1];
var builtin_cos(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? cos(val.num()):std::nan(""));
}
var builtin_tan(var* local, gc& ngc) {
var val = local[1];
var builtin_tan(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? tan(val.num()):std::nan(""));
}
var builtin_exp(var* local, gc& ngc) {
var val = local[1];
var builtin_exp(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? exp(val.num()):std::nan(""));
}
var builtin_lg(var* local, gc& ngc) {
var val = local[1];
var builtin_lg(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? log(val.num())/log(10.0):std::nan(""));
}
var builtin_ln(var* local, gc& ngc) {
var val = local[1];
var builtin_ln(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? log(val.num()):std::nan(""));
}
var builtin_sqrt(var* local, gc& ngc) {
var val = local[1];
var builtin_sqrt(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
return var::num(val.type==vm_num? sqrt(val.num()):std::nan(""));
}
var builtin_atan2(var* local, gc& ngc) {
var x = local[1];
var y = local[2];
var builtin_atan2(context* ctx, gc* ngc) {
auto x = ctx->localr[1];
auto y = ctx->localr[2];
if (x.type!=vm_num || y.type!=vm_num) {
return var::num(std::nan(""));
}
return var::num(atan2(y.num(), x.num()));
}
var builtin_isnan(var* local, gc& ngc) {
var x = local[1];
return (x.type==vm_num && std::isnan(x.num()))?one:zero;
var builtin_isnan(context* ctx, gc* ngc) {
auto x = ctx->localr[1];
return (x.type==vm_num && std::isnan(x.num()))? one:zero;
}
nasal_builtin_table math_lib_native[] = {

View File

@ -6,16 +6,16 @@
namespace nasal {
var builtin_pow(var*, gc&);
var builtin_sin(var*, gc&);
var builtin_cos(var*, gc&);
var builtin_tan(var*, gc&);
var builtin_exp(var*, gc&);
var builtin_lg(var*, gc&);
var builtin_ln(var*, gc&);
var builtin_sqrt(var*, gc&);
var builtin_atan2(var*, gc&);
var builtin_isnan(var*, gc&);
var builtin_pow(context*, gc*);
var builtin_sin(context*, gc*);
var builtin_cos(context*, gc*);
var builtin_tan(context*, gc*);
var builtin_exp(context*, gc*);
var builtin_lg(context*, gc*);
var builtin_ln(context*, gc*);
var builtin_sqrt(context*, gc*);
var builtin_atan2(context*, gc*);
var builtin_isnan(context*, gc*);
extern nasal_builtin_table math_lib_native[];

View File

@ -1,15 +1,15 @@
#pragma once
#ifndef __nasver
#define __nasver "11.0"
#define __nasver "11.1"
#endif
#include <cstdint>
#include <iostream>
#include <fstream>
#include <cstring>
#include <sstream>
#include <cmath>
#include <vector>
// abbreviation of some useful basic type
using i32 = std::int32_t;
@ -36,8 +36,7 @@ bool is_powerpc();
bool is_superh();
// virtual machine stack depth
// both global depth and value stack depth
// virtual machine stack depth, both global depth and value stack depth
const u32 STACK_DEPTH = 4096;
f64 hex2f(const char*);
@ -54,8 +53,6 @@ f64 dec2f(const char*);
f64 str2num(const char*);
i32 utf8_hdchk(const char);
std::string chrhex(const char);
std::string rawstr(const std::string&, const usize maxlen=0);
std::string rawstr(const std::string&, const usize maxlen = 0);
}
#include "nasal_gc.h"
}

View File

@ -77,7 +77,7 @@ public:
virtual void accept(ast_visitor*);
};
class call:public expr {
class call: public expr {
public:
call(const span& location, expr_type node_type):
expr(location, node_type) {}
@ -85,7 +85,7 @@ public:
virtual void accept(ast_visitor*);
};
class null_expr:public expr {
class null_expr: public expr {
public:
null_expr(const span& location):
expr(location, expr_type::ast_null) {}
@ -93,7 +93,7 @@ public:
void accept(ast_visitor*) override;
};
class nil_expr:public expr {
class nil_expr: public expr {
public:
nil_expr(const span& location):
expr(location, expr_type::ast_nil) {}
@ -101,7 +101,7 @@ public:
void accept(ast_visitor*) override;
};
class number_literal:public expr {
class number_literal: public expr {
private:
f64 number;
@ -113,7 +113,7 @@ public:
void accept(ast_visitor*) override;
};
class string_literal:public expr {
class string_literal: public expr {
private:
std::string content;
@ -125,7 +125,7 @@ public:
void accept(ast_visitor*) override;
};
class identifier:public expr {
class identifier: public expr {
private:
std::string name;
@ -137,7 +137,7 @@ public:
void accept(ast_visitor*) override;
};
class bool_literal:public expr {
class bool_literal: public expr {
private:
bool flag;
@ -149,7 +149,7 @@ public:
void accept(ast_visitor*) override;
};
class vector_expr:public expr {
class vector_expr: public expr {
private:
std::vector<expr*> elements;
@ -162,7 +162,7 @@ public:
void accept(ast_visitor*) override;
};
class hash_expr:public expr {
class hash_expr: public expr {
private:
std::vector<hash_pair*> members;
@ -175,7 +175,7 @@ public:
void accept(ast_visitor*) override;
};
class hash_pair:public expr {
class hash_pair: public expr {
private:
std::string name;
expr* value;
@ -192,7 +192,7 @@ public:
void accept(ast_visitor*) override;
};
class function:public expr {
class function: public expr {
private:
std::vector<parameter*> parameter_list;
code_block* block;
@ -209,7 +209,7 @@ public:
void accept(ast_visitor*) override;
};
class code_block:public expr {
class code_block: public expr {
private:
std::vector<expr*> expressions;
@ -222,7 +222,7 @@ public:
void accept(ast_visitor*) override;
};
class parameter:public expr {
class parameter: public expr {
public:
enum class param_type {
normal_parameter,
@ -249,7 +249,7 @@ public:
void accept(ast_visitor*) override;
};
class ternary_operator:public expr {
class ternary_operator: public expr {
private:
expr* condition;
expr* left;
@ -269,7 +269,7 @@ public:
void accept(ast_visitor*) override;
};
class binary_operator:public expr {
class binary_operator: public expr {
public:
enum class binary_type {
add,
@ -317,7 +317,7 @@ public:
void accept(ast_visitor*) override;
};
class unary_operator:public expr {
class unary_operator: public expr {
public:
enum class unary_type {
negative,
@ -344,7 +344,7 @@ public:
void accept(ast_visitor*) override;
};
class call_expr:public expr {
class call_expr: public expr {
private:
expr* first;
std::vector<call*> calls;
@ -361,7 +361,7 @@ public:
void accept(ast_visitor*) override;
};
class call_hash:public call {
class call_hash: public call {
private:
std::string field;
@ -374,7 +374,7 @@ public:
void accept(ast_visitor*) override;
};
class call_vector:public call {
class call_vector: public call {
private:
std::vector<slice_vector*> calls;
@ -387,7 +387,7 @@ public:
void accept(ast_visitor*) override;
};
class call_function:public call {
class call_function: public call {
private:
std::vector<expr*> args;
@ -400,7 +400,7 @@ public:
void accept(ast_visitor*) override;
};
class slice_vector:public expr {
class slice_vector: public expr {
private:
expr* begin;
expr* end;
@ -417,7 +417,7 @@ public:
void accept(ast_visitor*) override;
};
class definition_expr:public expr {
class definition_expr: public expr {
private:
identifier* variable_name;
multi_identifier* variables;
@ -441,7 +441,7 @@ public:
void accept(ast_visitor*) override;
};
class assignment_expr:public expr {
class assignment_expr: public expr {
public:
enum class assign_type {
equal,
@ -474,7 +474,7 @@ public:
void accept(ast_visitor*) override;
};
class multi_identifier:public expr {
class multi_identifier: public expr {
private:
std::vector<identifier*> variables;
@ -487,7 +487,7 @@ public:
void accept(ast_visitor*) override;
};
class tuple_expr:public expr {
class tuple_expr: public expr {
private:
std::vector<expr*> elements;
@ -500,7 +500,7 @@ public:
void accept(ast_visitor*) override;
};
class multi_assign:public expr {
class multi_assign: public expr {
private:
tuple_expr* tuple;
expr* value;
@ -517,7 +517,7 @@ public:
void accept(ast_visitor*) override;
};
class while_expr:public expr {
class while_expr: public expr {
private:
expr* condition;
code_block* block;
@ -534,7 +534,7 @@ public:
void accept(ast_visitor*) override;
};
class for_expr:public expr {
class for_expr: public expr {
private:
expr* initializing;
expr* condition;
@ -558,24 +558,28 @@ public:
void accept(ast_visitor*) override;
};
class iter_expr:public expr {
class iter_expr: public expr {
private:
bool is_iterator_definition;
identifier* name;
call_expr* call;
public:
iter_expr(const span& location):
expr(location, expr_type::ast_iter),
is_iterator_definition(false),
name(nullptr), call(nullptr) {}
~iter_expr() override;
void set_name(identifier* node) {name = node;}
void set_call(call_expr* node) {call = node;}
void set_is_definition(bool flag) {is_iterator_definition = flag;}
identifier* get_name() {return name;}
call_expr* get_call() {return call;}
bool is_definition() const {return is_iterator_definition;}
void accept(ast_visitor*) override;
};
class forei_expr:public expr {
class forei_expr: public expr {
public:
enum class forei_loop_type {
foreach,
@ -605,7 +609,7 @@ public:
void accept(ast_visitor*) override;
};
class condition_expr:public expr {
class condition_expr: public expr {
private:
if_expr* if_stmt;
std::vector<if_expr*> elsif_stmt;
@ -625,7 +629,7 @@ public:
void accept(ast_visitor*) override;
};
class if_expr:public expr {
class if_expr: public expr {
private:
expr* condition;
code_block* block;
@ -642,7 +646,7 @@ public:
void accept(ast_visitor*) override;
};
class continue_expr:public expr {
class continue_expr: public expr {
public:
continue_expr(const span& location):
expr(location, expr_type::ast_continue) {}
@ -650,7 +654,7 @@ public:
void accept(ast_visitor*) override;
};
class break_expr:public expr {
class break_expr: public expr {
public:
break_expr(const span& location):
expr(location, expr_type::ast_break) {}
@ -658,7 +662,7 @@ public:
void accept(ast_visitor*) override;
};
class return_expr:public expr {
class return_expr: public expr {
private:
expr* value;

View File

@ -3,33 +3,34 @@
namespace nasal {
var builtin_print(var* local, gc& ngc) {
for(auto& i : local[1].vec().elems) {
var builtin_print(context* ctx, gc* ngc) {
for(auto& i : ctx->localr[1].vec().elems) {
std::cout << i;
}
std::cout << std::flush;
return nil;
}
var builtin_println(var* local, gc& ngc) {
for(auto& i : local[1].vec().elems) {
var builtin_println(context* ctx, gc* ngc) {
for(auto& i : ctx->localr[1].vec().elems) {
std::cout << i;
}
std::cout << std::endl;
return nil;
}
var builtin_exit(var* local, gc& ngc) {
std::exit(local[1].num());
var builtin_exit(context* ctx, gc* ngc) {
std::exit(ctx->localr[1].num());
return nil;
}
var builtin_abort(var* local, gc& ngc) {
var builtin_abort(context* ctx, gc* ngc) {
std::abort();
return nil;
}
var builtin_append(var* local, gc& ngc) {
var builtin_append(context* ctx, gc* ngc) {
auto local = ctx->localr;
var vec = local[1];
var elem = local[2];
if (vec.type!=vm_vec) {
@ -42,7 +43,8 @@ var builtin_append(var* local, gc& ngc) {
return nil;
}
var builtin_setsize(var* local, gc& ngc) {
var builtin_setsize(context* ctx, gc* ngc) {
auto local = ctx->localr;
var vec = local[1];
var size = local[2];
if (vec.type!=vm_vec) {
@ -55,17 +57,18 @@ var builtin_setsize(var* local, gc& ngc) {
return nil;
}
var builtin_system(var* local, gc& ngc) {
var str = local[1];
var builtin_system(context* ctx, gc* ngc) {
auto str = ctx->localr[1];
if (str.type!=vm_str) {
return var::num(-1);
}
return var::num(static_cast<f64>(system(str.str().c_str())));
}
var builtin_input(var* local, gc& ngc) {
var builtin_input(context* ctx, gc* ngc) {
auto local = ctx->localr;
var end = local[1];
var ret = ngc.alloc(vm_str);
var ret = ngc->alloc(vm_str);
if (end.type!=vm_str || end.str().length()>1 || !end.str().length()) {
std::cin >> ret.str();
} else {
@ -74,7 +77,8 @@ var builtin_input(var* local, gc& ngc) {
return ret;
}
var builtin_split(var* local, gc& ngc) {
var builtin_split(context* ctx, gc* ngc) {
auto local = ctx->localr;
var delimeter = local[1];
var str = local[2];
if (delimeter.type!=vm_str) {
@ -87,34 +91,34 @@ var builtin_split(var* local, gc& ngc) {
const auto& s = str.str();
// avoid being sweeped
var res = ngc.temp = ngc.alloc(vm_vec);
auto res = ngc->temp = ngc->alloc(vm_vec);
auto& vec = res.vec().elems;
if (!deli.length()) {
for(auto i : s) {
vec.push_back(ngc.newstr(i));
vec.push_back(ngc->newstr(i));
}
ngc.temp = nil;
ngc->temp = nil;
return res;
}
usize last = 0;
usize pos = s.find(deli, 0);
while(pos!=std::string::npos) {
if (pos>last) {
vec.push_back(ngc.newstr(s.substr(last, pos-last)));
vec.push_back(ngc->newstr(s.substr(last, pos-last)));
}
last = pos+deli.length();
pos = s.find(deli, last);
}
if (last!=s.length()) {
vec.push_back(ngc.newstr(s.substr(last)));
vec.push_back(ngc->newstr(s.substr(last)));
}
ngc.temp = nil;
ngc->temp = nil;
return res;
}
var builtin_rand(var* local, gc& ngc) {
var val = local[1];
var builtin_rand(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
if (val.type!=vm_num && val.type!=vm_nil) {
return nas_err("rand", "\"seed\" must be nil or number");
}
@ -129,65 +133,70 @@ var builtin_rand(var* local, gc& ngc) {
return var::num(num);
}
var builtin_id(var* local, gc& ngc) {
var val = local[1];
var builtin_id(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
std::stringstream ss;
ss << "0";
if (val.type>vm_num) {
ss << "x" << std::hex;
ss << reinterpret_cast<u64>(val.val.gcobj) << std::dec;
}
return ngc.newstr(ss.str());
return ngc->newstr(ss.str());
}
var builtin_int(var* local, gc& ngc) {
var val = local[1];
var builtin_int(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
if (val.type!=vm_num && val.type!=vm_str) {
return nil;
}
return var::num(static_cast<f64>(static_cast<i32>(val.tonum())));
return var::num(static_cast<f64>(static_cast<i32>(val.to_num())));
}
var builtin_floor(var* local, gc& ngc) {
var val = local[1];
return var::num(std::floor(val.num()));
var builtin_floor(context* ctx, gc* ngc) {
auto value = ctx->localr[1];
return var::num(std::floor(value.num()));
}
var builtin_num(var* local, gc& ngc) {
var val = local[1];
var builtin_ceil(context* ctx, gc* ngc) {
auto value = ctx->localr[1];
return var::num(std::ceil(value.num()));
}
var builtin_num(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
if (val.type==vm_num) {
return val;
}
if (val.type!=vm_str) {
return nil;
}
f64 res = val.tonum();
auto res = val.to_num();
if (std::isnan(res)) {
return nil;
}
return var::num(res);
}
var builtin_pop(var* local, gc& ngc) {
var val = local[1];
var builtin_pop(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
if (val.type!=vm_vec) {
return nas_err("pop", "\"vec\" must be vector");
}
auto& vec = val.vec().elems;
if (vec.size()) {
var tmp = vec.back();
auto tmp = vec.back();
vec.pop_back();
return tmp;
}
return nil;
}
var builtin_str(var* local, gc& ngc) {
return ngc.newstr(local[1].tostr());
var builtin_str(context* ctx, gc* ngc) {
return ngc->newstr(ctx->localr[1].to_str());
}
var builtin_size(var* local, gc& ngc) {
var val = local[1];
var builtin_size(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
f64 num = 0;
switch(val.type) {
case vm_num: num = val.num(); break;
@ -199,16 +208,17 @@ var builtin_size(var* local, gc& ngc) {
return var::num(num);
}
var builtin_time(var* local, gc& ngc) {
var val = local[1];
var builtin_time(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
if (val.type!=vm_num) {
return nas_err("time", "\"begin\" must be number");
}
time_t begin = (time_t)val.num();
auto begin = static_cast<time_t>(val.num());
return var::num(static_cast<f64>(time(&begin)));
}
var builtin_contains(var* local, gc& ngc) {
var builtin_contains(context* ctx, gc* ngc) {
auto local = ctx->localr;
var hash = local[1];
var key = local[2];
if (hash.type!=vm_hash || key.type!=vm_str) {
@ -217,7 +227,8 @@ var builtin_contains(var* local, gc& ngc) {
return hash.hash().elems.count(key.str())? one:zero;
}
var builtin_delete(var* local, gc& ngc) {
var builtin_delete(context* ctx, gc* ngc) {
auto local = ctx->localr;
var hash = local[1];
var key = local[2];
if (hash.type!=vm_hash) {
@ -232,58 +243,60 @@ var builtin_delete(var* local, gc& ngc) {
return nil;
}
var builtin_keys(var* local, gc& ngc) {
var hash = local[1];
var builtin_keys(context* ctx, gc* ngc) {
auto hash = ctx->localr[1];
if (hash.type!=vm_hash && hash.type!=vm_map) {
return nas_err("keys", "\"hash\" must be hash");
}
// avoid being sweeped
var res = ngc.temp = ngc.alloc(vm_vec);
auto res = ngc->temp = ngc->alloc(vm_vec);
auto& vec = res.vec().elems;
if (hash.type==vm_hash) {
for(const auto& iter : hash.hash().elems) {
vec.push_back(ngc.newstr(iter.first));
vec.push_back(ngc->newstr(iter.first));
}
} else {
for(const auto& iter : hash.map().mapper) {
vec.push_back(ngc.newstr(iter.first));
vec.push_back(ngc->newstr(iter.first));
}
}
ngc.temp=nil;
ngc->temp=nil;
return res;
}
var builtin_die(var* local, gc& ngc) {
return nas_err("error", local[1].tostr());
var builtin_die(context* ctx, gc* ngc) {
return nas_err("error", ctx->localr[1].to_str());
}
var builtin_find(var* local, gc& ngc) {
var builtin_find(context* ctx, gc* ngc) {
auto local = ctx->localr;
var needle = local[1];
var haystack = local[2];
usize pos = haystack.tostr().find(needle.tostr());
usize pos = haystack.to_str().find(needle.to_str());
if (pos==std::string::npos) {
return var::num(-1.0);
}
return var::num(static_cast<f64>(pos));
}
var builtin_type(var* local, gc& ngc) {
switch(local[1].type) {
case vm_none: return ngc.newstr("undefined");
case vm_nil: return ngc.newstr("nil");
case vm_num: return ngc.newstr("num");
case vm_str: return ngc.newstr("str");
case vm_vec: return ngc.newstr("vec");
case vm_hash: return ngc.newstr("hash");
case vm_func: return ngc.newstr("func");
case vm_obj: return ngc.newstr("obj");
case vm_co: return ngc.newstr("coroutine");
case vm_map: return ngc.newstr("namespace");
var builtin_type(context* ctx, gc* ngc) {
switch(ctx->localr[1].type) {
case vm_none: return ngc->newstr("undefined");
case vm_nil: return ngc->newstr("nil");
case vm_num: return ngc->newstr("num");
case vm_str: return ngc->newstr("str");
case vm_vec: return ngc->newstr("vec");
case vm_hash: return ngc->newstr("hash");
case vm_func: return ngc->newstr("func");
case vm_obj: return ngc->newstr("obj");
case vm_co: return ngc->newstr("coroutine");
case vm_map: return ngc->newstr("namespace");
}
return nil;
}
var builtin_substr(var* local, gc& ngc) {
var builtin_substr(context* ctx, gc* ngc) {
auto local = ctx->localr;
var str = local[1];
var beg = local[2];
var len = local[3];
@ -301,10 +314,11 @@ var builtin_substr(var* local, 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(var* local, gc& ngc) {
var builtin_streq(context* ctx, gc* ngc) {
auto local = ctx->localr;
var a = local[1];
var b = local[2];
return var::num(static_cast<f64>(
@ -312,7 +326,8 @@ var builtin_streq(var* local, gc& ngc) {
));
}
var builtin_left(var* local, gc& ngc) {
var builtin_left(context* ctx, gc* ngc) {
auto local = ctx->localr;
var str = local[1];
var len = local[2];
if (str.type!=vm_str) {
@ -322,12 +337,13 @@ var builtin_left(var* local, gc& ngc) {
return nas_err("left", "\"length\" must be number");
}
if (len.num()<0) {
return ngc.newstr("");
return ngc->newstr("");
}
return ngc.newstr(str.str().substr(0, len.num()));
return ngc->newstr(str.str().substr(0, len.num()));
}
var builtin_right(var* local, gc& ngc) {
var builtin_right(context* ctx, gc* ngc) {
auto local = ctx->localr;
var str = local[1];
var len = local[2];
if (str.type!=vm_str) {
@ -344,10 +360,11 @@ var builtin_right(var* local, gc& ngc) {
if (length<0) {
length = 0;
}
return ngc.newstr(str.str().substr(srclen-length, srclen));
return ngc->newstr(str.str().substr(srclen-length, srclen));
}
var builtin_cmp(var* local, gc& ngc) {
var builtin_cmp(context* ctx, gc* ngc) {
auto local = ctx->localr;
var a = local[1];
var b = local[2];
if (a.type!=vm_str || b.type!=vm_str) {
@ -359,7 +376,7 @@ var builtin_cmp(var* local, gc& ngc) {
)));
}
var builtin_chr(var* local, gc& ngc) {
var builtin_chr(context* ctx, gc* ngc) {
const char* extend[] = {
""," ","","ƒ","","","","",
"ˆ","","Š","","Œ"," ","Ž"," ",
@ -378,34 +395,40 @@ var builtin_chr(var* local, gc& ngc) {
"ð","ñ","ò","ó","ô","õ","ö","÷",
"ø","ù","ú","û","ü","ý","þ","ÿ"
};
i32 num = local[1].num();
auto num = static_cast<i32>(ctx->localr[1].num());
if (0<=num && num<128) {
return ngc.newstr((char)num);
return ngc->newstr((char)num);
} else if (128<=num && num<256) {
return ngc.newstr(extend[num-128]);
return ngc->newstr(extend[num-128]);
}
return ngc.newstr(" ");
return ngc->newstr(" ");
}
var builtin_char(var* local, gc& ngc) {
return ngc.newstr((unsigned char)local[1].num());
var builtin_char(context* ctx, gc* ngc) {
return ngc->newstr(static_cast<unsigned char>(ctx->localr[1].num()));
}
var builtin_values(var* local, gc& ngc) {
var hash = local[1];
if (hash.type!=vm_hash) {
return nas_err("values", "\"hash\" must be hash");
var builtin_values(context* ctx, gc* ngc) {
auto hash = ctx->localr[1];
if (hash.type!=vm_hash && hash.type!=vm_map) {
return nas_err("values", "\"hash\" must be hash or namespace");
}
var vec = ngc.alloc(vm_vec);
auto vec = ngc->alloc(vm_vec);
auto& v = vec.vec().elems;
for(auto& i : hash.hash().elems) {
v.push_back(i.second);
if (hash.type==vm_hash) {
for(auto& i : hash.hash().elems) {
v.push_back(i.second);
}
} else {
for(auto& i : hash.map().mapper) {
v.push_back(*i.second);
}
}
return vec;
}
var builtin_sleep(var* local, gc& ngc) {
var val = local[1];
var builtin_sleep(context* ctx, gc* ngc) {
auto val = ctx->localr[1];
if (val.type!=vm_num) {
return nil;
}
@ -421,36 +444,36 @@ var builtin_sleep(var* local, gc& ngc) {
return nil;
}
var builtin_platform(var* local, gc& ngc) {
var builtin_platform(context* ctx, gc* ngc) {
if (is_windows()) {
return ngc.newstr("windows");
return ngc->newstr("windows");
} else if (is_linux()) {
return ngc.newstr("linux");
return ngc->newstr("linux");
} else if (is_macos()) {
return ngc.newstr("macOS");
return ngc->newstr("macOS");
}
return ngc.newstr("unknown");
return ngc->newstr("unknown");
}
var builtin_arch(var* local, gc& ngc) {
var builtin_arch(context* ctx, gc* ngc) {
if (is_x86()) {
return ngc.newstr("x86");
return ngc->newstr("x86");
} else if (is_x86_64()) {
return ngc.newstr("x86-64");
return ngc->newstr("x86-64");
} else if (is_amd64()) {
return ngc.newstr("amd64");
return ngc->newstr("amd64");
} else if (is_arm()) {
return ngc.newstr("arm");
return ngc->newstr("arm");
} else if (is_aarch64()) {
return ngc.newstr("aarch64");
return ngc->newstr("aarch64");
} else if (is_ia64()) {
return ngc.newstr("ia64");
return ngc->newstr("ia64");
} else if (is_powerpc()) {
return ngc.newstr("powerpc");
return ngc->newstr("powerpc");
} else if (is_superh()) {
return ngc.newstr("superh");
return ngc->newstr("superh");
}
return ngc.newstr("unknown");
return ngc->newstr("unknown");
}
// md5 related functions
@ -536,64 +559,64 @@ std::string md5(const std::string& src) {
return tohex(atmp)+tohex(btmp)+tohex(ctmp)+tohex(dtmp);
}
var builtin_md5(var* local, gc& ngc) {
var str = local[1];
var builtin_md5(context* ctx, gc* ngc) {
auto str = ctx->localr[1];
if (str.type!=vm_str) {
return nas_err("md5", "\"str\" must be string");
}
return ngc.newstr(md5(str.str()));
return ngc->newstr(md5(str.str()));
}
var builtin_millisec(var* local, gc& ngc) {
var builtin_millisec(context* ctx, gc* ngc) {
f64 res = std::chrono::duration_cast<std::chrono::milliseconds>
(std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
return var::num(res);
}
var builtin_gcextend(var* local, gc& ngc) {
var type = local[1];
var builtin_gcextend(context* ctx, gc* ngc) {
auto type = ctx->localr[1];
if (type.type!=vm_str) {
return nil;
}
auto& s = type.str();
const auto& s = type.str();
if (s=="str") {
ngc.extend(vm_str);
ngc->extend(vm_str);
} else if (s=="vec") {
ngc.extend(vm_vec);
ngc->extend(vm_vec);
} else if (s=="hash") {
ngc.extend(vm_hash);
ngc->extend(vm_hash);
} else if (s=="func") {
ngc.extend(vm_func);
ngc->extend(vm_func);
} else if (s=="upval") {
ngc.extend(vm_upval);
ngc->extend(vm_upval);
} else if (s=="obj") {
ngc.extend(vm_obj);
ngc->extend(vm_obj);
} else if (s=="co") {
ngc.extend(vm_co);
ngc->extend(vm_co);
}
return nil;
}
var builtin_gcinfo(var* local, gc& ngc) {
var builtin_gcinfo(context* ctx, gc* ngc) {
auto den = std::chrono::high_resolution_clock::duration::period::den;
var res = ngc.alloc(vm_hash);
var res = ngc->alloc(vm_hash);
double total = 0;
for(u32 i = 0; i<gc_type_size; ++i) {
total += ngc.gcnt[i];
total += 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);
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);
return res;
}
var builtin_logtime(var* local, gc& ngc) {
var builtin_logtime(context* ctx, gc* ngc) {
time_t t = time(nullptr);
tm* tm_t = localtime(&t);
char s[64];
@ -606,19 +629,19 @@ var builtin_logtime(var* local, gc& ngc) {
tm_t->tm_min,
tm_t->tm_sec
);
return ngc.newstr(s);
return ngc->newstr(s);
}
var builtin_ghosttype(var* local, gc& ngc) {
var arg = local[1];
var builtin_ghosttype(context* ctx, gc* ngc) {
auto arg = ctx->localr[1];
if (arg.type!=vm_obj) {
return nas_err("ghosttype", "this is not a ghost object.");
}
const auto& name = arg.obj().get_ghost_name();
const auto& name = arg.ghost().get_ghost_name();
if (!name.length()) {
return var::num(reinterpret_cast<u64>(arg.obj().ptr));
return var::num(reinterpret_cast<u64>(arg.ghost().pointer));
}
return ngc.newstr(name);
return ngc->newstr(name);
}
nasal_builtin_table builtin[] = {
@ -635,6 +658,7 @@ nasal_builtin_table builtin[] = {
{"__id", builtin_id},
{"__int", builtin_int},
{"__floor", builtin_floor},
{"__ceil", builtin_ceil},
{"__num", builtin_num},
{"__pop", builtin_pop},
{"__str", builtin_str},

View File

@ -1,8 +1,13 @@
#pragma once
#include "nasal.h"
#include "nasal_type.h"
#include "nasal_gc.h"
#ifdef _WIN32
#include <windows.h>
#endif
#ifdef _MSC_VER
#pragma warning (disable:4566) // i know i'm using utf-8, fuck you
#pragma warning (disable:4244)
@ -24,56 +29,57 @@
namespace nasal {
var builtin_print(var*, gc&);
var builtin_println(var*, gc&);
var builtin_exit(var*, gc&);
var builtin_abort(var*, gc&);
var builtin_append(var*, gc&);
var builtin_setsize(var*, gc&);
var builtin_system(var*, gc&);
var builtin_input(var*, gc&);
var builtin_split(var*, gc&);
var builtin_rand(var*, gc&);
var builtin_id(var*, gc&);
var builtin_int(var*, gc&);
var builtin_floor(var*, gc&);
var builtin_num(var*, gc&);
var builtin_pop(var*, gc&);
var builtin_str(var*, gc&);
var builtin_size(var*, gc&);
var builtin_time(var*, gc&);
var builtin_contains(var*, gc&);
var builtin_delete(var*, gc&);
var builtin_keys(var*, gc&);
var builtin_die(var*, gc&);
var builtin_find(var*, gc&);
var builtin_type(var*, gc&);
var builtin_substr(var*, gc&);
var builtin_streq(var*, gc&);
var builtin_left(var*, gc&);
var builtin_right(var*, gc&);
var builtin_cmp(var*, gc&);
var builtin_chr(var*, gc&);
var builtin_char(var*, gc&);
var builtin_values(var*, gc&);
var builtin_sleep(var*, gc&);
var builtin_platform(var*, gc&);
var builtin_arch(var*, gc&);
var builtin_print(context*, gc*);
var builtin_println(context*, gc*);
var builtin_exit(context*, gc*);
var builtin_abort(context*, gc*);
var builtin_append(context*, gc*);
var builtin_setsize(context*, gc*);
var builtin_system(context*, gc*);
var builtin_input(context*, gc*);
var builtin_split(context*, gc*);
var builtin_rand(context*, gc*);
var builtin_id(context*, gc*);
var builtin_int(context*, gc*);
var builtin_floor(context*, gc*);
var builtin_ceil(context*, gc*);
var builtin_num(context*, gc*);
var builtin_pop(context*, gc*);
var builtin_str(context*, gc*);
var builtin_size(context*, gc*);
var builtin_time(context*, gc*);
var builtin_contains(context*, gc*);
var builtin_delete(context*, gc*);
var builtin_keys(context*, gc*);
var builtin_die(context*, gc*);
var builtin_find(context*, gc*);
var builtin_type(context*, gc*);
var builtin_substr(context*, gc*);
var builtin_streq(context*, gc*);
var builtin_left(context*, gc*);
var builtin_right(context*, gc*);
var builtin_cmp(context*, gc*);
var builtin_chr(context*, gc*);
var builtin_char(context*, gc*);
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(var*, gc&);
var builtin_millisec(var*, gc&);
var builtin_gcextend(var*, gc&);
var builtin_gcinfo(var*, gc&);
var builtin_logtime(var*, gc&);
var builtin_ghosttype(var*, gc&);
var builtin_md5(context*, gc*);
var builtin_millisec(context*, gc*);
var builtin_gcextend(context*, gc*);
var builtin_gcinfo(context*, gc*);
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}
struct nasal_builtin_table {
const char* name;
var (*func)(var*, gc&);
var (*func)(context*, gc*);
};
extern nasal_builtin_table builtin[];

View File

@ -89,7 +89,7 @@ void codegen::find_symbol(code_block* node) {
experimental_namespace[i.location.file] = {};
}
// if in global scope, load global symbol into this namespace
auto scope = experimental_namespace.at(i.location.file);
auto& scope = experimental_namespace.at(i.location.file);
if (local.empty() && !scope.count(i.name)) {
scope.insert(i.name);
}
@ -835,13 +835,18 @@ void codegen::forei_gen(forei_expr* node) {
} else {
emit(op_feach, 0, node->get_location());
}
if (node->get_iterator()->get_name()) {
auto iterator_node = node->get_iterator();
if (iterator_node->is_definition()) {
// define a new iterator
auto name_node = node->get_iterator()->get_name();
const auto name_node = iterator_node->get_name();
const auto& str = name_node->get_name();
local.empty()?
emit(op_loadg, global_symbol_find(str), name_node->get_location()):
emit(op_loadl, local_symbol_find(str), name_node->get_location());
} else if (!iterator_node->is_definition() && iterator_node->get_name()) {
mcall(node->get_iterator()->get_name());
replace_left_assignment_with_load(node->get_iterator()->get_location());
} else {
// use exist variable as the iterator
mcall(node->get_iterator()->get_call());
@ -1303,7 +1308,7 @@ void codegen::print(std::ostream& out) {
out << std::hex << "<0x" << func_begin_stack.top() << std::dec << ">;\n";
// avoid two empty lines
if (c.op!=op_newf) {
out<<"\n";
out << "\n";
}
func_begin_stack.pop();
func_end_stack.pop();
@ -1322,7 +1327,7 @@ void codegen::print(std::ostream& out) {
}
// output bytecode
out << " " << codestream(c,i) << "\n";
out << " " << codestream(c, i) << "\n";
}
}

View File

@ -159,8 +159,8 @@ void dbg::step_info() {
begin = (ctx.pc>>3)==0? 0:((ctx.pc>>3)<<3);
end = (1+(ctx.pc>>3))<<3;
codestream::set(cnum, cstr, native.data(), files);
std::clog << "next bytecode:\n";
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) {
std::clog
<< (i==ctx.pc? back_white:reset)
@ -168,7 +168,7 @@ void dbg::step_info() {
<< codestream(bytecode[i], i)
<< reset << "\n";
}
stackinfo(10);
stack_info(10);
}
void dbg::interact() {
@ -181,10 +181,10 @@ void dbg::interact() {
if (do_profiling) {
return;
}
if ((bytecode[ctx.pc].fidx!=bk_fidx ||
bytecode[ctx.pc].line!=bk_line) && // break point
!next) {// next step
// is not break point and is not next stop command
const auto& code = bytecode[ctx.pc];
if ((code.fidx!=break_file_index || code.line!=break_line) && !next) {
return;
}
@ -194,28 +194,31 @@ void dbg::interact() {
while(true) {
std::clog << ">> ";
std::getline(std::cin, cmd);
auto res=parse(cmd);
auto res = parse(cmd);
if (res.size()==0) {
step_info();
} else if (res.size()==1) {
switch(get_cmd_type(res[0])) {
case dbg_cmd::cmd_help: help(); break;
case dbg_cmd::cmd_backtrace: traceback(); break;
case dbg_cmd::cmd_backtrace:
function_call_trace();
trace_back();
break;
case dbg_cmd::cmd_continue: return;
case dbg_cmd::cmd_list_file: list_file(); break;
case dbg_cmd::cmd_global: gstate(); break;
case dbg_cmd::cmd_local: lstate(); break;
case dbg_cmd::cmd_upval: ustate(); break;
case dbg_cmd::cmd_register: reginfo(); break;
case dbg_cmd::cmd_show_all: detail(); break;
case dbg_cmd::cmd_global: global_state(); break;
case dbg_cmd::cmd_local: local_state(); break;
case dbg_cmd::cmd_upval: upvalue_state(); break;
case dbg_cmd::cmd_register: register_info(); break;
case dbg_cmd::cmd_show_all: all_state_detail(); break;
case dbg_cmd::cmd_next: next = true; return;
case dbg_cmd::cmd_exit: std::exit(0);
default: err(); break;
}
} else if (res.size()==3 &&
get_cmd_type(res[0])==dbg_cmd::cmd_break_point) {
bk_fidx = file_index(res[1]);
if (bk_fidx==65535) {
break_file_index = file_index(res[1]);
if (break_file_index==65535) {
std::clog << "cannot find file named `" << res[1] << "`\n";
continue;
}
@ -223,7 +226,7 @@ void dbg::interact() {
if (tmp<=0) {
std::clog << "incorrect line number `" << res[2] << "`\n";
} else {
bk_line = tmp;
break_line = tmp;
}
} else {
err();
@ -243,9 +246,15 @@ void dbg::run(
const auto& file_list = linker.get_file_list();
fsize = file_list.size();
init(gen.strs(), gen.nums(), gen.natives(),
gen.codes(), gen.globals(),
file_list, argv);
init(
gen.strs(),
gen.nums(),
gen.natives(),
gen.codes(),
gen.globals(),
file_list,
argv
);
data.init(file_list);
std::vector<u32> code;

View File

@ -37,7 +37,7 @@ public:
}
};
class dbg:public vm {
class dbg: public vm {
private:
typedef void (dbg::*nasal_vm_func)();
const nasal_vm_func operand_function[op_ret + 1] = {
@ -139,8 +139,8 @@ private:
private:
bool next;
usize fsize;
u16 bk_fidx;
u32 bk_line;
u16 break_file_index;
u32 break_line;
error src;
private:
@ -158,8 +158,8 @@ private:
public:
dbg():
next(false), fsize(0),
bk_fidx(0), bk_line(0),
next(true), fsize(0),
break_file_index(0), break_line(0),
do_profiling(false) {}
void run(
const codegen&,

View File

@ -10,11 +10,8 @@ struct for_reset {
for_reset() {
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &scr);
}
static for_reset* singleton() {
static for_reset windows_set;
return &windows_set;
}
};
static for_reset windows_system_set;
#endif
std::ostream& back_white(std::ostream& s) {
@ -64,8 +61,10 @@ std::ostream& white(std::ostream& s) {
std::ostream& reset(std::ostream& s) {
#ifdef _WIN32
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
for_reset::singleton()->scr.wAttributes);
SetConsoleTextAttribute(
GetStdHandle(STD_OUTPUT_HANDLE),
windows_system_set.scr.wAttributes
);
#else
s << "\033[0m";
#endif

View File

@ -2,348 +2,6 @@
namespace nasal {
var nas_vec::get_val(const i32 n) {
i32 size = elems.size();
if (n<-size || n>=size) {
return var::none();
}
return elems[n>=0? n:n+size];
}
var* nas_vec::get_mem(const i32 n) {
i32 size = elems.size();
if (n<-size || n>=size) {
return nullptr;
}
return &elems[n>=0? n:n+size];
}
std::ostream& operator<<(std::ostream& out, nas_vec& vec) {
if (!vec.elems.size() || vec.printed) {
out << (vec.elems.size()? "[..]":"[]");
return out;
}
vec.printed = true;
usize iter = 0, size = vec.elems.size();
out << "[";
for(auto& i:vec.elems) {
out << i << ",]"[(++iter)==size];
}
vec.printed = false;
return out;
}
var nas_hash::get_val(const std::string& key) {
if (elems.count(key)) {
return elems.at(key);
} else if (!elems.count("parents")) {
return var::none();
}
var ret = var::none();
var val = elems.at("parents");
if (val.type!=vm_vec) {
return ret;
}
for(auto& i : val.vec().elems) {
if (i.type==vm_hash) {
ret = i.hash().get_val(key);
}
if (ret.type!=vm_none) {
return ret;
}
}
return ret;
}
var* nas_hash::get_mem(const std::string& key) {
if (elems.count(key)) {
return &elems.at(key);
} else if (!elems.count("parents")) {
return nullptr;
}
var* addr = nullptr;
var val = elems.at("parents");
if (val.type!=vm_vec) {
return addr;
}
for(auto& i : val.vec().elems) {
if (i.type==vm_hash) {
addr = i.hash().get_mem(key);
}
if (addr) {
return addr;
}
}
return addr;
}
std::ostream& operator<<(std::ostream& out, nas_hash& hash) {
if (!hash.elems.size() || hash.printed) {
out << (hash.elems.size()? "{..}":"{}");
return out;
}
hash.printed = true;
usize iter = 0, size = hash.elems.size();
out << "{";
for(auto& i : hash.elems) {
out << i.first << ":" << i.second << ",}"[(++iter)==size];
}
hash.printed = false;
return out;
}
void nas_func::clear() {
dpara = -1;
local.clear();
upval.clear();
keys.clear();
}
void nas_ghost::set(
const std::string& ghost_type_name,
destructor destructor_pointer,
void* ghost_pointer) {
type_name = ghost_type_name;
dtor_ptr = destructor_pointer;
ptr = ghost_pointer;
}
void nas_ghost::clear() {
// do nothing if pointer is null
if (!ptr) {
return;
}
// do clear pointer if destructor function pointer is null
if (!dtor_ptr) {
type_name = "";
ptr = nullptr;
return;
}
// do destruction
dtor_ptr(ptr);
type_name = "";
ptr = nullptr;
dtor_ptr = nullptr;
}
std::ostream& operator<<(std::ostream& out, const nas_ghost& ghost) {
out << "<object " << ghost.get_ghost_name();
out << " at 0x" << std::hex;
out << reinterpret_cast<u64>(ghost.ptr) << std::dec << ">";
return out;
}
void nas_co::clear() {
if (!ctx.stack) {
return;
}
for(u32 i = 0; i<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.top = ctx.stack;
ctx.funcr = var::nil();
ctx.upvalr = var::nil();
status = status::suspended;
}
std::ostream& operator<<(std::ostream& out, const nas_co& co) {
out << "<coroutine at 0x" << std::hex;
out << reinterpret_cast<u64>(&co) << std::dec << ">";
return out;
}
var nas_map::get_val(const std::string& key) {
if (mapper.count(key)) {
return *mapper.at(key);
}
return var::none();
}
var* nas_map::get_mem(const std::string& key) {
if (mapper.count(key)) {
return mapper.at(key);
}
return nullptr;
}
std::ostream& operator<<(std::ostream& out, nas_map& mp) {
if (!mp.mapper.size() || mp.printed) {
out << (mp.mapper.size()? "{..}":"{}");
return out;
}
mp.printed = true;
usize iter = 0, size = mp.mapper.size();
out << "{";
for(auto& i : mp.mapper) {
out << i.first << ":" << *i.second << ",}"[(++iter)==size];
}
mp.printed = false;
return out;
}
nas_val::nas_val(u8 val_type) {
mark = gc_status::collected;
type = val_type;
unmut = 0;
switch(val_type) {
case vm_str: ptr.str = new std::string; break;
case vm_vec: ptr.vec = new nas_vec; break;
case vm_hash: ptr.hash = new nas_hash; break;
case vm_func: ptr.func = new nas_func; break;
case vm_upval: ptr.upval = new nas_upval; break;
case vm_obj: ptr.obj = new nas_ghost; break;
case vm_co: ptr.co = new nas_co; break;
case vm_map: ptr.map = new nas_map; break;
}
}
nas_val::~nas_val() {
switch(type) {
case vm_str: delete ptr.str; break;
case vm_vec: delete ptr.vec; break;
case vm_hash: delete ptr.hash; break;
case vm_func: delete ptr.func; break;
case vm_upval:delete ptr.upval;break;
case vm_obj: delete ptr.obj; break;
case vm_co: delete ptr.co; break;
case vm_map: delete ptr.map; break;
}
type=vm_nil;
}
void nas_val::clear() {
switch(type) {
case vm_str: ptr.str->clear(); break;
case vm_vec: ptr.vec->elems.clear(); break;
case vm_hash: ptr.hash->elems.clear();break;
case vm_func: ptr.func->clear(); break;
case vm_upval:ptr.upval->clear(); break;
case vm_obj: ptr.obj->clear(); break;
case vm_co: ptr.co->clear(); break;
case vm_map: ptr.map->clear(); break;
}
}
f64 var::tonum() {
return type!=vm_str? val.num:str2num(str().c_str());
}
std::string var::tostr() {
if (type==vm_str) {
return str();
} else if (type==vm_num) {
std::string tmp=std::to_string(num());
tmp.erase(tmp.find_last_not_of('0')+1, std::string::npos);
tmp.erase(tmp.find_last_not_of('.')+1, std::string::npos);
return tmp;
}
return "";
}
std::ostream& operator<<(std::ostream& out, var& ref) {
switch(ref.type) {
case vm_none: out << "undefined"; break;
case vm_nil: out << "nil"; break;
case vm_num: out << ref.val.num; break;
case vm_str: out << ref.str(); break;
case vm_vec: out << ref.vec(); break;
case vm_hash: out << ref.hash(); break;
case vm_func: out << "func(..) {..}"; break;
case vm_obj: out << ref.obj(); break;
case vm_co: out << ref.co(); break;
case vm_map: out << ref.map(); break;
}
return out;
}
bool var::objchk(const std::string& name) {
return type==vm_obj && obj().type_name==name && obj().ptr;
}
var var::none() {
return {vm_none, static_cast<u32>(0)};
}
var var::nil() {
return {vm_nil, static_cast<u32>(0)};
}
var var::ret(u32 pc) {
return {vm_ret, pc};
}
var var::cnt(i64 n) {
return {vm_cnt, n};
}
var var::num(f64 n) {
return {vm_num, n};
}
var var::gcobj(nas_val* p) {
return {p->type, p};
}
var var::addr(var* p) {
return {vm_addr, p};
}
var* var::addr() {
return val.addr;
}
u32 var::ret() {
return val.ret;
}
i64& var::cnt() {
return val.cnt;
}
f64 var::num() {
return val.num;
}
std::string& var::str() {
return *val.gcobj->ptr.str;
}
nas_vec& var::vec() {
return *val.gcobj->ptr.vec;
}
nas_hash& var::hash() {
return *val.gcobj->ptr.hash;
}
nas_func& var::func() {
return *val.gcobj->ptr.func;
}
nas_upval& var::upval() {
return *val.gcobj->ptr.upval;
}
nas_ghost& var::obj() {
return *val.gcobj->ptr.obj;
}
nas_co& var::co() {
return *val.gcobj->ptr.co;
}
nas_map& var::map() {
return *val.gcobj->ptr.map;
}
void gc::do_mark_sweep() {
using clk = std::chrono::high_resolution_clock;
auto begin = clk::now();
@ -418,13 +76,13 @@ void gc::mark_context_root(std::vector<var>& bfs_queue) {
}
}
// scan now running context, this context maybe related to coroutine or main
for(var* i = rctx->stack; i<=rctx->top; ++i) {
for(var* i = running_context->stack; i<=running_context->top; ++i) {
if (i->type>vm_num) {
bfs_queue.push_back(*i);
}
}
bfs_queue.push_back(rctx->funcr);
bfs_queue.push_back(rctx->upvalr);
bfs_queue.push_back(running_context->funcr);
bfs_queue.push_back(running_context->upvalr);
bfs_queue.push_back(temp);
if (!cort) {
@ -432,13 +90,13 @@ void gc::mark_context_root(std::vector<var>& bfs_queue) {
}
// coroutine is running, so scan main process stack from mctx
for(var* i = mctx.stack; i<=mctx.top; ++i) {
for(var* i = main_context.stack; i<=main_context.top; ++i) {
if (i->type>vm_num) {
bfs_queue.push_back(*i);
}
}
bfs_queue.push_back(mctx.funcr);
bfs_queue.push_back(mctx.upvalr);
bfs_queue.push_back(main_context.funcr);
bfs_queue.push_back(main_context.upvalr);
}
void gc::mark_var(std::vector<var>& bfs_queue, var& value) {
@ -556,7 +214,7 @@ void gc::init(
continue;
}
strs[i] = var::gcobj(new nas_val(vm_str));
strs[i].val.gcobj->unmut = 1;
strs[i].val.gcobj->unmutable = 1;
strs[i].str() = constant_strings[i];
}
@ -568,7 +226,7 @@ void gc::init(
continue;
}
env_argv[i] = var::gcobj(new nas_val(vm_str));
env_argv[i].val.gcobj->unmut = 1;
env_argv[i].val.gcobj->unmutable = 1;
env_argv[i].str() = argv[i];
}
}
@ -694,39 +352,34 @@ var gc::alloc(u8 type) {
return ret;
}
void gc::ctxchg(nas_co& co) {
void gc::context_change(nas_co* co) {
// store running state to main context
mctx = *rctx;
main_context = *running_context;
// restore coroutine context state
*rctx = co.ctx;
*running_context = co->ctx;
// set coroutine pointer
cort = &co;
cort = co;
// set coroutine state to running
cort->status = nas_co::status::running;
}
void gc::ctxreserve() {
void gc::context_reserve() {
// pc=0 means this coroutine is finished
cort->status = rctx->pc?
cort->status = running_context->pc?
nas_co::status::suspended:
nas_co::status::dead;
// store running state to coroutine
cort->ctx = *rctx;
cort->ctx = *running_context;
// restore main context state
*rctx = mctx;
*running_context = main_context;
// set coroutine pointer to nullptr
cort = nullptr;
}
var nas_err(const std::string& error_function_name, const std::string& info) {
std::cerr << "[vm] " << error_function_name << ": " << info << "\n";
return var::none();
}
}

View File

@ -7,293 +7,28 @@
#pragma warning (disable:4102)
#endif
#ifndef _MSC_VER
#include <unistd.h>
#include <dirent.h>
#else
#include <io.h>
#include <direct.h>
#endif
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif
#include <iomanip>
#include <vector>
#include <unordered_map>
#include <chrono>
#include <algorithm>
#include <thread>
#include <cstring>
#include <sstream>
#include "nasal.h"
#include "nasal_type.h"
namespace nasal {
enum vm_type:u8 {
/* none-gc object */
vm_none = 0,
vm_cnt,
vm_addr,
vm_ret,
vm_nil,
vm_num,
/* gc object */
vm_str,
vm_vec,
vm_hash,
vm_func,
vm_upval,
vm_obj,
vm_co,
vm_map // for globals and namespaces
};
const u32 gc_type_size = vm_map-vm_str+1;
struct nas_vec; // vector
struct nas_hash; // hashmap(dict)
struct nas_func; // function(lambda)
struct nas_upval; // upvalue
struct nas_ghost; // objects
struct nas_co; // coroutine
struct nas_map; // mapper
struct nas_val; // nas_val includes gc-managed types
struct var {
public:
u8 type = vm_none;
union {
u32 ret;
i64 cnt;
f64 num;
var* addr;
nas_val* gcobj;
} val;
private:
var(u8 t, u32 pc) {type = t; val.ret = pc;}
var(u8 t, i64 ct) {type = t; val.cnt = ct;}
var(u8 t, f64 n) {type = t; val.num = n;}
var(u8 t, var* p) {type = t; val.addr = p;}
var(u8 t, nas_val* p) {type = t; val.gcobj = p;}
public:
var() = default;
var(const var&) = default;
bool operator==(const var& nr) const {
return type==nr.type && val.gcobj==nr.val.gcobj;
}
bool operator!=(const var& nr) const {
return type!=nr.type || val.gcobj!=nr.val.gcobj;
}
// number and string can be translated to each other
f64 tonum();
std::string tostr();
bool objchk(const std::string&);
// create new var object
static var none();
static var nil();
static var ret(u32);
static var cnt(i64);
static var num(f64);
static var gcobj(nas_val*);
static var addr(var*);
// get content
var* addr();
u32 ret();
i64& cnt();
f64 num();
std::string& str();
nas_vec& vec();
nas_hash& hash();
nas_func& func();
nas_upval& upval();
nas_ghost& obj();
nas_co& co();
nas_map& map();
};
struct nas_vec {
std::vector<var> elems;
// mark if this is printed, avoid stackoverflow
bool printed;
nas_vec():printed(false) {}
usize size() const {return elems.size();}
var get_val(const i32);
var* get_mem(const i32);
};
struct nas_hash {
std::unordered_map<std::string, var> elems;
// mark if this is printed, avoid stackoverflow
bool printed;
nas_hash(): printed(false) {}
usize size() const {return elems.size();}
var get_val(const std::string&);
var* get_mem(const std::string&);
};
struct nas_func {
i32 dpara; // dynamic parameter name index in hash.
u32 entry; // pc will set to entry-1 to call this function
u32 psize; // used to load default parameters to a new function
u32 lsize; // used to expand memory space for local values on stack
std::vector<var> local; // local scope with default value(var)
std::vector<var> upval; // closure
std::unordered_map<u32,u32> keys; // parameter table, u32 begins from 1
nas_func(): dpara(-1), entry(0), psize(0), lsize(0) {}
void clear();
};
struct nas_upval {
public:
/* on stack, use these variables */
bool onstk;
u32 size;
var* stk;
/* not on stack, use this */
std::vector<var> elems;
public:
nas_upval(): onstk(true), size(0), stk(nullptr) {}
var& operator[](usize n) {
return onstk? stk[n]:elems[n];
}
void clear() {
onstk = true;
elems.clear();
size = 0;
}
};
struct nas_ghost {
private:
using destructor=void (*)(void*);
public:
std::string type_name;
destructor dtor_ptr;
void* ptr;
public:
nas_ghost(): type_name(""), dtor_ptr(nullptr), ptr(nullptr) {}
~nas_ghost() {clear();}
void set(const std::string&, destructor, void*);
void clear();
public:
const std::string& get_ghost_name() const {
return type_name;
}
};
struct context {
u32 pc = 0;
var* localr = nullptr;
var* memr = nullptr;
var funcr = var::nil();
var upvalr = var::nil();
var* canary = nullptr;
var* stack = nullptr;
var* top = nullptr;
};
struct nas_co {
enum class status:u32 {
suspended,
running,
dead
};
context ctx;
status status;
nas_co() {
ctx.stack = new var[STACK_DEPTH];
clear();
}
~nas_co() {
delete[] ctx.stack;
}
void clear();
};
struct nas_map {
bool printed = false;
std::unordered_map<std::string, var*> mapper;
nas_map() {}
void clear() {
mapper.clear();
}
var get_val(const std::string&);
var* get_mem(const std::string&);
};
struct nas_val {
enum class gc_status:u8 {
uncollected = 0,
collected,
found
};
gc_status mark;
u8 type; // value type
u8 unmut; // used to mark if a string is unmutable
union {
std::string* str;
nas_vec* vec;
nas_hash* hash;
nas_func* func;
nas_upval* upval;
nas_ghost* obj;
nas_co* co;
nas_map* map;
} ptr;
nas_val(u8);
~nas_val();
void clear();
};
std::ostream& operator<<(std::ostream&, nas_vec&);
std::ostream& operator<<(std::ostream&, nas_hash&);
std::ostream& operator<<(std::ostream&, nas_map&);
std::ostream& operator<<(std::ostream&, const nas_ghost&);
std::ostream& operator<<(std::ostream&, const nas_co&);
std::ostream& operator<<(std::ostream&, var&);
const var zero = var::num(0);
const var one = var::num(1);
const var nil = var::nil();
struct gc {
/* main context temporary storage */
context mctx;
context main_context;
/* global storage */
var* main_context_global = nullptr;
usize main_context_global_size = 0;
/* runtime context */
context* rctx = nullptr;
context* running_context = nullptr;
nas_co* cort = nullptr; // running coroutine
/* temporary space used in native/module functions */
@ -327,7 +62,7 @@ struct gc {
i64 max_sweep_time = 0;
void set(context* _ctx, var* _global, usize _size) {
rctx = _ctx;
running_context = _ctx;
main_context_global = _global;
main_context_global_size = _size;
}
@ -353,8 +88,8 @@ public:
void clear();
void info() const;
var alloc(const u8);
void ctxchg(nas_co&);
void ctxreserve();
void context_change(nas_co*);
void context_reserve();
public:
var newstr(char c) {
@ -364,21 +99,18 @@ public:
}
var newstr(const char* buff) {
var s=alloc(vm_str);
s.str() = buff;
var s = alloc(vm_str);
s.str() = std::string(buff);
return s;
}
var newstr(const std::string& buff) {
var s=alloc(vm_str);
var s = alloc(vm_str);
s.str() = buff;
return s;
}
};
// use to print error log and return error value
var nas_err(const std::string&, const std::string&);
// module function type
typedef var (*module_func)(var*, usize, gc*);

View File

@ -1,6 +1,8 @@
#include "nasal_import.h"
#include "symbol_finder.h"
#include <memory>
namespace nasal {
linker::linker():
@ -196,8 +198,10 @@ code_block* linker::import_regular_file(call_expr* node) {
return new code_block({0, 0, 0, 0, filename});
}
if (check_self_import(filename)) {
err.err("link", "self-referenced module <" + filename + ">:\n" +
" reference path: " + generate_self_import_path(filename));
err.err("link",
"self-referenced module <" + filename + ">:\n" +
" reference path: " + generate_self_import_path(filename)
);
return new code_block({0, 0, 0, 0, filename});
}
exist(filename);
@ -206,16 +210,19 @@ code_block* linker::import_regular_file(call_expr* node) {
// start importing...
if (lex.scan(filename).geterr()) {
err.err("link", "error occurred when analysing <" + filename + ">");
return new code_block({0, 0, 0, 0, filename});
}
if (par.compile(lex).geterr()) {
err.err("link", "error occurred when analysing <" + filename + ">");
return new code_block({0, 0, 0, 0, filename});
}
auto tmp = par.swap(nullptr);
// check if tmp has 'import'
auto res = load(tmp, find(filename));
auto parse_result = par.swap(nullptr);
// check if parse result has 'import'
auto result = load(parse_result, find(filename));
module_load_stack.pop_back();
return res;
return result;
}
code_block* linker::import_nasal_lib() {
@ -234,52 +241,88 @@ code_block* linker::import_nasal_lib() {
// start importing...
if (lex.scan(filename).geterr()) {
err.err("link", "error occurred when analysing library <" + filename + ">");
err.err("link",
"error occurred when analysing library <" + filename + ">"
);
return new code_block({0, 0, 0, 0, filename});
}
if (par.compile(lex).geterr()) {
err.err("link", "error occurred when analysing library <" + filename + ">");
err.err("link",
"error occurred when analysing library <" + filename + ">"
);
return new code_block({0, 0, 0, 0, filename});
}
auto tmp = par.swap(nullptr);
// check if tmp has 'import'
return load(tmp, find(filename));
auto parse_result = par.swap(nullptr);
// check if library has 'import' (in fact it should not)
return load(parse_result, find(filename));
}
std::string linker::generate_module_name(const std::string& filename) {
auto error_name = "error_generated@[" + filename + "]";
auto pos = filename.find_last_of(".nas");
if (pos==std::string::npos) {
std::string linker::generate_module_name(const std::string& file_path) {
auto error_name = "module@[" + file_path + "]";
if (!file_path.length()) {
return error_name;
}
pos -= 4;
auto split_pos = filename.find_last_of("/");
if (split_pos==std::string::npos) {
split_pos = filename.find_last_of("\\");
// check file suffix and get file suffix position
auto suffix_position = file_path.find(".nas");
if (suffix_position==std::string::npos) {
err.warn("link",
"get invalid module name from <" + file_path + ">, " +
"will not be easily accessed. " +
"\".nas\" suffix is required."
);
return error_name;
}
auto res = split_pos==std::string::npos?
filename.substr(0, pos + 1):
filename.substr(split_pos + 1, pos - split_pos);
if (!res.length()) {
err.warn("link", "get empty module name from <" + filename + ">, " +
"will not be easily accessed.");
if (suffix_position+4!=file_path.length()) {
err.warn("link",
"get invalid module name from <" + file_path + ">, " +
"will not be easily accessed. " +
"only one \".nas\" suffix is required in the path."
);
return error_name;
}
if (res.length() && '0' <= res[0] && res[0] <= '9') {
err.warn("link", "get module <" + res + "> from <" + filename + ">, " +
"will not be easily accessed.");
// only get the file name as module name, directory path is not included
auto split_position = file_path.find_last_of("/");
// find "\\" in windows platform
if (split_position==std::string::npos) {
split_position = file_path.find_last_of("\\");
}
if (res.length() && res.find(".")!=std::string::npos) {
err.warn("link", "get module <" + res + "> from <" + filename + ">, " +
"will not be easily accessed.");
// split file path to get module name
auto module_name = split_position==std::string::npos?
file_path.substr(0, suffix_position):
file_path.substr(split_position+1, suffix_position-split_position-1);
// check validation of module name
if (!module_name.length()) {
err.warn("link",
"get empty module name from <" + file_path + ">, " +
"will not be easily accessed."
);
}
return res;
if (module_name.length() && '0' <= module_name[0] && module_name[0] <= '9') {
err.warn("link",
"get module <" + module_name + "> from <" + file_path + ">, " +
"will not be easily accessed."
);
}
if (module_name.length() && module_name.find(".")!=std::string::npos) {
err.warn("link",
"get module <" + module_name + "> from <" + file_path + ">, " +
"will not be easily accessed."
);
}
return module_name;
}
return_expr* linker::generate_module_return(code_block* block) {
auto sf = new symbol_finder;
auto res = new return_expr(block->get_location());
auto finder = std::unique_ptr<symbol_finder>(new symbol_finder);
auto result = new return_expr(block->get_location());
auto value = new hash_expr(block->get_location());
res->set_value(value);
for(const auto& i : sf->do_find(block)) {
result->set_value(value);
for(const auto& i : finder->do_find(block)) {
auto pair = new hash_pair(block->get_location());
// do not export symbol begins with '_'
if (i.name.length() && i.name[0]=='_') {
@ -289,8 +332,7 @@ return_expr* linker::generate_module_return(code_block* block) {
pair->set_value(new identifier(block->get_location(), i.name));
value->add_member(pair);
}
delete sf;
return res;
return result;
}
definition_expr* linker::generate_module_definition(code_block* block) {

View File

@ -120,7 +120,8 @@ f64 oct2f(const char* str) {
// but this also makes 0.1+0.2==0.3,
// not another result that you may get in other languages.
f64 dec2f(const char* str) {
f64 ret = 0, negative = 1, num_pow = 0;
f64 ret = 0, num_pow = 0;
bool negative = false;
while('0'<=*str && *str<='9') {
ret = ret*10+(*str++-'0');
}
@ -147,7 +148,7 @@ f64 dec2f(const char* str) {
return nan("");
}
if (*str=='-' || *str=='+') {
negative = (*str++=='-'? -1:1);
negative = (*str++=='-');
}
if (!*str) {
return nan("");
@ -159,7 +160,9 @@ f64 dec2f(const char* str) {
if (*str) {
return nan("");
}
return ret*std::pow(10, negative*num_pow);
return negative?
ret*std::pow(10, 1-num_pow)*0.1:
ret*std::pow(10, num_pow-1)*10;
}
f64 str2num(const char* str) {

View File

@ -28,13 +28,13 @@ const char* opname[] = {
};
void codestream::set(
const f64* num_buff,
const std::string* str_buff,
const nasal_builtin_table* native_table_ptr,
const f64* number_list,
const std::string* string_list,
const nasal_builtin_table* native_table,
const std::string* file_list) {
nums = num_buff;
strs = str_buff;
natives = native_table_ptr;
const_number = number_list;
const_string = string_list;
natives = native_table;
files = file_list;
}
@ -62,24 +62,24 @@ void codestream::dump(std::ostream& out) const {
case op_addeqc: case op_subeqc:
case op_muleqc:case op_diveqc:
out << hex << "0x" << num << dec
<< " (" << nums[num] << ")"; break;
<< " (" << const_number[num] << ")"; break;
case op_lnkeqc:
out << hex << "0x" << num << dec
<< " (" << rawstr(strs[num], 16) << ")"; break;
<< " (" << rawstr(const_string[num], 16) << ")"; break;
case op_addecp: case op_subecp:
case op_mulecp: case op_divecp:
out << hex << "0x" << num << dec
<< " (" << nums[num] << ") sp-1"; break;
<< " (" << const_number[num] << ") sp-1"; break;
case op_lnkecp:
out << hex << "0x" << num << dec
<< " (" << rawstr(strs[num], 16) << ") sp-1"; break;
<< " (" << 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
<< " (" << nums[num] << ")"; break;
<< " (" << const_number[num] << ")"; break;
case op_callvi: case op_newv:
case op_callfv: case op_repl:
case op_intl: case op_findex:
@ -103,7 +103,7 @@ void codestream::dump(std::ostream& out) const {
case op_mcallh: case op_para:
case op_deft: case op_dyn:
out << hex << "0x" << num << dec
<< " (" << rawstr(strs[num], 16) << ")"; break;
<< " (" << rawstr(const_string[num], 16) << ")"; break;
default:
if (files) {
out << hex << "0x" << num << dec;

View File

@ -111,8 +111,8 @@ class codestream {
private:
opcode code;
const u32 index;
inline static const f64* nums = nullptr;
inline static const std::string* strs = nullptr;
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;

View File

@ -971,20 +971,26 @@ forei_expr* parse::forei_loop() {
iter_expr* parse::iter_gen() {
auto node = new iter_expr(toks[ptr].loc);
// definition
if (lookahead(tok::var)) {
match(tok::var);
node->set_name(id());
node->set_is_definition(true);
update_location(node);
return node;
}
// single symbol call
auto id_node = id();
if (!is_call(toks[ptr].type)) {
node->set_name(id_node);
update_location(node);
return node;
}
// call expression
auto tmp = new call_expr(id_node->get_location());
tmp->set_first(id());
tmp->set_first(id_node);
while(is_call(toks[ptr].type)) {
tmp->add_call(call_scalar());
}

355
src/nasal_type.cpp Normal file
View File

@ -0,0 +1,355 @@
#include "nasal_type.h"
#include <cstring>
#include <sstream>
namespace nasal {
var nas_vec::get_value(const i32 index) {
i32 size = elems.size();
if (index<-size || index>=size) {
return var::none();
}
return elems[index>=0? index:index+size];
}
var* nas_vec::get_memory(const i32 index) {
i32 size = elems.size();
if (index<-size || index>=size) {
return nullptr;
}
return &elems[index>=0? index:index+size];
}
std::ostream& operator<<(std::ostream& out, nas_vec& vec) {
if (!vec.elems.size() || vec.printed) {
out << (vec.elems.size()? "[..]":"[]");
return out;
}
vec.printed = true;
usize iter = 0, size = vec.elems.size();
out << "[";
for(auto& i:vec.elems) {
out << i << ",]"[(++iter)==size];
}
vec.printed = false;
return out;
}
var nas_hash::get_value(const std::string& key) {
if (elems.count(key)) {
return elems.at(key);
} else if (!elems.count("parents")) {
return var::none();
}
var ret = var::none();
var val = elems.at("parents");
if (val.type!=vm_vec) {
return ret;
}
for(auto& i : val.vec().elems) {
if (i.type==vm_hash) {
ret = i.hash().get_value(key);
}
if (ret.type!=vm_none) {
return ret;
}
}
return ret;
}
var* nas_hash::get_memory(const std::string& key) {
if (elems.count(key)) {
return &elems.at(key);
} else if (!elems.count("parents")) {
return nullptr;
}
var* addr = nullptr;
var val = elems.at("parents");
if (val.type!=vm_vec) {
return addr;
}
for(auto& i : val.vec().elems) {
if (i.type==vm_hash) {
addr = i.hash().get_memory(key);
}
if (addr) {
return addr;
}
}
return addr;
}
std::ostream& operator<<(std::ostream& out, nas_hash& hash) {
if (!hash.elems.size() || hash.printed) {
out << (hash.elems.size()? "{..}":"{}");
return out;
}
hash.printed = true;
usize iter = 0, size = hash.elems.size();
out << "{";
for(auto& i : hash.elems) {
out << i.first << ":" << i.second << ",}"[(++iter)==size];
}
hash.printed = false;
return out;
}
void nas_func::clear() {
dynamic_parameter_index = -1;
local.clear();
upval.clear();
keys.clear();
}
void nas_ghost::set(
const std::string& ghost_type_name,
destructor destructor_pointer,
void* ghost_pointer) {
type_name = ghost_type_name;
destructor_function = destructor_pointer;
pointer = ghost_pointer;
}
void nas_ghost::clear() {
// do nothing if pointer is null
if (!pointer) {
return;
}
// do clear pointer if destructor function pointer is null
if (!destructor_function) {
type_name = "";
pointer = nullptr;
return;
}
// do destruction
destructor_function(pointer);
type_name = "";
pointer = nullptr;
destructor_function = nullptr;
}
std::ostream& operator<<(std::ostream& out, const nas_ghost& ghost) {
out << "<object " << ghost.get_ghost_name();
out << " at 0x" << std::hex;
out << reinterpret_cast<u64>(ghost.pointer) << std::dec << ">";
return out;
}
void nas_co::clear() {
if (!ctx.stack) {
return;
}
for(u32 i = 0; i<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.top = ctx.stack;
ctx.funcr = var::nil();
ctx.upvalr = var::nil();
status = status::suspended;
}
std::ostream& operator<<(std::ostream& out, const nas_co& co) {
out << "<coroutine at 0x" << std::hex;
out << reinterpret_cast<u64>(&co) << std::dec << ">";
return out;
}
var nas_map::get_value(const std::string& key) {
if (mapper.count(key)) {
return *mapper.at(key);
}
return var::none();
}
var* nas_map::get_memory(const std::string& key) {
if (mapper.count(key)) {
return mapper.at(key);
}
return nullptr;
}
std::ostream& operator<<(std::ostream& out, nas_map& mp) {
if (!mp.mapper.size() || mp.printed) {
out << (mp.mapper.size()? "{..}":"{}");
return out;
}
mp.printed = true;
usize iter = 0, size = mp.mapper.size();
out << "{";
for(auto& i : mp.mapper) {
out << i.first << ":" << *i.second << ",}"[(++iter)==size];
}
mp.printed = false;
return out;
}
nas_val::nas_val(u8 val_type) {
mark = gc_status::collected;
type = val_type;
unmutable = 0;
switch(val_type) {
case vm_str: ptr.str = new std::string; break;
case vm_vec: ptr.vec = new nas_vec; break;
case vm_hash: ptr.hash = new nas_hash; break;
case vm_func: ptr.func = new nas_func; break;
case vm_upval: ptr.upval = new nas_upval; break;
case vm_obj: ptr.obj = new nas_ghost; break;
case vm_co: ptr.co = new nas_co; break;
case vm_map: ptr.map = new nas_map; break;
}
}
nas_val::~nas_val() {
switch(type) {
case vm_str: delete ptr.str; break;
case vm_vec: delete ptr.vec; break;
case vm_hash: delete ptr.hash; break;
case vm_func: delete ptr.func; break;
case vm_upval:delete ptr.upval; break;
case vm_obj: delete ptr.obj; break;
case vm_co: delete ptr.co; break;
case vm_map: delete ptr.map; break;
}
type=vm_nil;
}
void nas_val::clear() {
switch(type) {
case vm_str: ptr.str->clear(); break;
case vm_vec: ptr.vec->elems.clear(); break;
case vm_hash: ptr.hash->elems.clear(); break;
case vm_func: ptr.func->clear(); break;
case vm_upval:ptr.upval->clear(); break;
case vm_obj: ptr.obj->clear(); break;
case vm_co: ptr.co->clear(); break;
case vm_map: ptr.map->clear(); break;
}
}
f64 var::to_num() {
return type!=vm_str? val.num:str2num(str().c_str());
}
std::string var::to_str() {
if (type==vm_str) {
return str();
} else if (type==vm_num) {
std::string tmp = std::to_string(num());
tmp.erase(tmp.find_last_not_of('0')+1, std::string::npos);
tmp.erase(tmp.find_last_not_of('.')+1, std::string::npos);
return tmp;
}
return "";
}
std::ostream& operator<<(std::ostream& out, var& ref) {
switch(ref.type) {
case vm_none: out << "undefined"; break;
case vm_nil: out << "nil"; break;
case vm_num: out << ref.val.num; break;
case vm_str: out << ref.str(); break;
case vm_vec: out << ref.vec(); break;
case vm_hash: out << ref.hash(); break;
case vm_func: out << "func(..) {..}"; break;
case vm_obj: out << ref.ghost(); break;
case vm_co: out << ref.co(); break;
case vm_map: out << ref.map(); break;
}
return out;
}
bool var::object_check(const std::string& name) {
return type==vm_obj && ghost().type_name==name && ghost().pointer;
}
var var::none() {
return {vm_none, static_cast<u32>(0)};
}
var var::nil() {
return {vm_nil, static_cast<u32>(0)};
}
var var::ret(u32 pc) {
return {vm_ret, pc};
}
var var::cnt(i64 n) {
return {vm_cnt, n};
}
var var::num(f64 n) {
return {vm_num, n};
}
var var::gcobj(nas_val* p) {
return {p->type, p};
}
var var::addr(var* p) {
return {vm_addr, p};
}
var* var::addr() {
return val.addr;
}
u32 var::ret() const {
return val.ret;
}
i64& var::cnt() {
return val.cnt;
}
f64 var::num() const {
return val.num;
}
std::string& var::str() {
return *val.gcobj->ptr.str;
}
nas_vec& var::vec() {
return *val.gcobj->ptr.vec;
}
nas_hash& var::hash() {
return *val.gcobj->ptr.hash;
}
nas_func& var::func() {
return *val.gcobj->ptr.func;
}
nas_upval& var::upval() {
return *val.gcobj->ptr.upval;
}
nas_ghost& var::ghost() {
return *val.gcobj->ptr.obj;
}
nas_co& var::co() {
return *val.gcobj->ptr.co;
}
nas_map& var::map() {
return *val.gcobj->ptr.map;
}
var nas_err(const std::string& error_function_name, const std::string& info) {
std::cerr << "[vm] " << error_function_name << ": " << info << "\n";
return var::none();
}
}

269
src/nasal_type.h Normal file
View File

@ -0,0 +1,269 @@
#pragma once
#include "nasal.h"
#include <vector>
#include <unordered_map>
namespace nasal {
enum vm_type:u8 {
/* none-gc object */
vm_none = 0, // error type
vm_cnt, // counter for forindex/foreach loop
vm_addr, // var* address
vm_ret, // return addres(program counter)
vm_nil, // nil
vm_num, // number
/* gc object */
vm_str, // string
vm_vec, // vector
vm_hash, // hashmap(dict)
vm_func, // function(lambda)
vm_upval, // upvalue
vm_obj, // ghost type
vm_co, // coroutine
vm_map, // for globals and namespaces
/* mark type range */
vm_type_size_max
};
// size of gc object type
const u32 gc_type_size = vm_type_size_max-vm_str;
// basic types
struct nas_vec; // vector
struct nas_hash; // hashmap(dict)
struct nas_func; // function(lambda)
struct nas_upval; // upvalue
struct nas_ghost; // objects
struct nas_co; // coroutine
struct nas_map; // mapper
// union type
struct nas_val; // nas_val includes gc-managed types
struct var {
public:
u8 type = vm_none;
union {
u32 ret;
i64 cnt;
f64 num;
var* addr;
nas_val* gcobj;
} val;
private:
var(u8 t, u32 pc) {type = t; val.ret = pc;}
var(u8 t, i64 ct) {type = t; val.cnt = ct;}
var(u8 t, f64 n) {type = t; val.num = n;}
var(u8 t, var* p) {type = t; val.addr = p;}
var(u8 t, nas_val* p) {type = t; val.gcobj = p;}
public:
var() = default;
var(const var&) = default;
bool operator==(const var& nr) const {
return type==nr.type && val.gcobj==nr.val.gcobj;
}
bool operator!=(const var& nr) const {
return type!=nr.type || val.gcobj!=nr.val.gcobj;
}
// number and string can be translated to each other
f64 to_num();
std::string to_str();
bool object_check(const std::string&);
// create new var object
static var none();
static var nil();
static var ret(u32);
static var cnt(i64);
static var num(f64);
static var gcobj(nas_val*);
static var addr(var*);
// get value
var* addr();
u32 ret() const;
i64& cnt();
f64 num() const;
std::string& str();
nas_vec& vec();
nas_hash& hash();
nas_func& func();
nas_upval& upval();
nas_ghost& ghost();
nas_co& co();
nas_map& map();
};
struct nas_vec {
std::vector<var> elems;
// mark if this is printed, avoid stack overflow
bool printed = false;
usize size() const {return elems.size();}
var get_value(const i32);
var* get_memory(const i32);
};
struct nas_hash {
std::unordered_map<std::string, var> elems;
// mark if this is printed, avoid stack overflow
bool printed = false;
usize size() const {return elems.size();}
var get_value(const std::string&);
var* get_memory(const std::string&);
};
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
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
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;
nas_func():
dynamic_parameter_index(-1), entry(0),
parameter_size(0), local_size(0) {}
void clear();
};
struct nas_upval {
public:
/* on stack, use these variables */
bool on_stack;
u32 size;
var* stack_frame_offset;
/* not on stack, use this */
std::vector<var> elems;
public:
nas_upval(): on_stack(true), size(0), stack_frame_offset(nullptr) {}
var& operator[](usize n) {
return on_stack? stack_frame_offset[n]:elems[n];
}
void clear() {
on_stack = true;
elems.clear();
size = 0;
}
};
struct nas_ghost {
private:
using destructor = void (*)(void*);
public:
std::string type_name;
destructor destructor_function;
void* pointer;
public:
nas_ghost():
type_name(""), destructor_function(nullptr), pointer(nullptr) {}
~nas_ghost() {clear();}
void set(const std::string&, destructor, void*);
void clear();
public:
const auto& get_ghost_name() const {return type_name;}
};
struct context {
u32 pc = 0;
var* localr = nullptr;
var* memr = nullptr;
var funcr = var::nil();
var upvalr = var::nil();
var* canary = nullptr;
var* stack = nullptr;
var* top = nullptr;
};
struct nas_co {
enum class status:u32 {
suspended,
running,
dead
};
context ctx;
status status;
nas_co() {
ctx.stack = new var[STACK_DEPTH];
clear();
}
~nas_co() {
delete[] ctx.stack;
}
void clear();
};
struct nas_map {
bool printed = false;
std::unordered_map<std::string, var*> mapper;
void clear() {
mapper.clear();
}
var get_value(const std::string&);
var* get_memory(const std::string&);
};
struct nas_val {
enum class gc_status:u8 {
uncollected = 0,
collected,
found
};
gc_status mark;
u8 type; // value type
u8 unmutable; // used to mark if a string is unmutable
union {
std::string* str;
nas_vec* vec;
nas_hash* hash;
nas_func* func;
nas_upval* upval;
nas_ghost* obj;
nas_co* co;
nas_map* map;
} ptr;
nas_val(u8);
~nas_val();
void clear();
};
std::ostream& operator<<(std::ostream&, nas_vec&);
std::ostream& operator<<(std::ostream&, nas_hash&);
std::ostream& operator<<(std::ostream&, nas_map&);
std::ostream& operator<<(std::ostream&, const nas_ghost&);
std::ostream& operator<<(std::ostream&, const nas_co&);
std::ostream& operator<<(std::ostream&, var&);
const var zero = var::num(0);
const var one = var::num(1);
const var nil = var::nil();
// use to print error log and return error value
var nas_err(const std::string&, const std::string&);
}

View File

@ -11,14 +11,14 @@ void vm::init(
const std::vector<std::string>& filenames,
const std::vector<std::string>& argv
) {
cnum = nums.data();
cstr = strs.data();
const_number = nums.data();
const_string = strs.data();
bytecode = code.data();
files = filenames.data();
global_size = global_symbol.size();
/* set native functions */
native = natives;
native_function = natives;
/* set context and global */
if (!is_repl_mode || first_exec_flag) {
@ -64,7 +64,7 @@ void vm::context_and_global_init() {
}
}
void vm::valinfo(var& val) {
void vm::value_info(var& val) {
const auto p = reinterpret_cast<u64>(val.val.gcobj);
switch(val.type) {
case vm_none: std::clog << "| null |"; break;
@ -93,7 +93,7 @@ void vm::valinfo(var& val) {
<< " val}"; break;
case vm_obj: std::clog << "| obj | <0x" << std::hex << p
<< "> obj:0x"
<< reinterpret_cast<u64>(val.obj().ptr)
<< reinterpret_cast<u64>(val.ghost().pointer)
<< std::dec; break;
case vm_co: std::clog << "| co | <0x" << std::hex << p
<< std::dec << "> coroutine"; break;
@ -106,22 +106,84 @@ void vm::valinfo(var& val) {
std::clog << "\n";
}
void vm::traceback() {
void vm::function_detail_info(const nas_func& func) {
std::clog << "func@0x";
std::clog << std::hex << reinterpret_cast<u64>(&func) << std::dec;
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;
}
std::clog << "(";
for(const auto& key : argument_list) {
std::clog << key;
if (key != argument_list.back()) {
std::clog << ", ";
}
}
if (func.dynamic_parameter_index>=0) {
std::clog << (argument_list.size()? ", ":"");
std::clog << const_string[func.dynamic_parameter_index] << "...";
}
std::clog << ") ";
std::clog << "{entry: 0x" << std::hex << func.entry << std::dec << "}";
}
void vm::function_call_trace() {
var* bottom = ctx.stack;
var* top = ctx.top;
std::stack<u32> ret;
// generate trace back
std::stack<const nas_func*> functions;
for(var* i = bottom; i<=top; ++i) {
if (i->type==vm_func && i-1>=bottom && (i-1)->type==vm_ret) {
functions.push(&i->func());
}
}
if (functions.empty()) {
return;
}
std::clog << "\ncall trace " << (ngc.cort? "(coroutine)":"(main)") << "\n";
const nas_func* last = nullptr;
u32 same = 0;
for(auto func = last; !functions.empty(); functions.pop()) {
func = functions.top();
if (last==func) {
++same;
continue;
} else if (same) {
std::clog << " --> " << same << " same call(s)\n";
same = 0;
}
last = func;
std::clog << " call ";
function_detail_info(*func);
std::clog << "\n";
}
if (same) {
std::clog << " --> " << same << " same call(s)\n";
}
}
void vm::trace_back() {
// var* bottom = ctx.stack;
// var* top = ctx.top;
// generate trace back
std::stack<u32> ret;
for(var* i = ctx.stack; i<=ctx.top; ++i) {
if (i->type==vm_ret && i->ret()!=0) {
ret.push(i->ret());
}
}
ret.push(ctx.pc); // store the position program crashed
std::clog << "trace back ("
<< (ngc.cort? "coroutine":"main")
<< ")\n";
codestream::set(cnum, cstr, native.data(), files);
std::clog << "\ntrace back " << (ngc.cort? "(coroutine)":"(main)") << "\n";
codestream::set(const_number, const_string, native_function.data(), files);
for(u32 p = 0, same = 0, prev = 0xffffffff; !ret.empty(); prev = p, ret.pop()) {
if ((p = ret.top())==prev) {
++same;
@ -132,17 +194,17 @@ void vm::traceback() {
<< std::setw(6) << std::setfill('0')
<< prev << std::dec << " "
<< same << " same call(s)\n";
same = 0;
}
same = 0;
std::clog << " " << codestream(bytecode[p], p) << "\n";
}
// the first called place has no same calls
}
void vm::stackinfo(const u32 limit = 10) {
void vm::stack_info(const u32 limit = 10) {
var* top = ctx.top;
var* bottom = ctx.stack;
std::clog << "stack (0x" << std::hex << reinterpret_cast<u64>(bottom);
std::clog << "\nstack (0x" << std::hex << reinterpret_cast<u64>(bottom);
std::clog << std::dec << ", 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) {
@ -150,12 +212,12 @@ void vm::stackinfo(const u32 limit = 10) {
<< std::setw(6) << std::setfill('0')
<< static_cast<u64>(top-bottom) << std::dec
<< " ";
valinfo(top[0]);
value_info(top[0]);
}
}
void vm::reginfo() {
std::clog << "registers (" << (ngc.cort? "coroutine":"main")
void vm::register_info() {
std::clog << "\nregisters (" << (ngc.cort? "coroutine":"main")
<< ")\n" << std::hex
<< " [pc ] | pc | 0x" << ctx.pc << "\n"
<< " [global] | addr | 0x"
@ -169,45 +231,45 @@ void vm::reginfo() {
<< " [top ] | addr | 0x"
<< reinterpret_cast<u64>(ctx.top) << "\n"
<< std::dec;
std::clog << " [funcr ] "; valinfo(ctx.funcr);
std::clog << " [upval ] "; valinfo(ctx.upvalr);
std::clog << " [funcr ] "; value_info(ctx.funcr);
std::clog << " [upval ] "; value_info(ctx.upvalr);
}
void vm::gstate() {
void vm::global_state() {
if (!global_size || global[0].type==vm_none) {
return;
}
std::clog << "global (0x" << std::hex
std::clog << "\nglobal (0x" << std::hex
<< reinterpret_cast<u64>(global) << ")\n" << std::dec;
for(usize i = 0; i<global_size; ++i) {
std::clog << " 0x" << std::hex << std::setw(6)
<< std::setfill('0') << i << std::dec
<< " ";
valinfo(global[i]);
value_info(global[i]);
}
}
void vm::lstate() {
if (!ctx.localr || !ctx.funcr.func().lsize) {
void vm::local_state() {
if (!ctx.localr || !ctx.funcr.func().local_size) {
return;
}
const u32 lsize = ctx.funcr.func().lsize;
std::clog << "local (0x" << std::hex << reinterpret_cast<u64>(ctx.localr)
const u32 lsize = ctx.funcr.func().local_size;
std::clog << "\nlocal (0x" << std::hex << reinterpret_cast<u64>(ctx.localr)
<< " <+" << static_cast<u64>(ctx.localr-ctx.stack)
<< ">)\n" << std::dec;
for(u32 i = 0; i<lsize; ++i) {
std::clog << " 0x" << std::hex << std::setw(6)
<< std::setfill('0') << i << std::dec
<< " ";
valinfo(ctx.localr[i]);
value_info(ctx.localr[i]);
}
}
void vm::ustate() {
void vm::upvalue_state() {
if (ctx.funcr.type==vm_nil || ctx.funcr.func().upval.empty()) {
return;
}
std::clog << "upvalue\n";
std::clog << "\nupvalue\n";
auto& upval = ctx.funcr.func().upval;
for(u32 i = 0; i<upval.size(); ++i) {
std::clog << " -> upval[" << i << "]:\n";
@ -216,26 +278,121 @@ void vm::ustate() {
std::clog << " 0x" << std::hex << std::setw(6)
<< std::setfill('0') << j << std::dec
<< " ";
valinfo(uv[j]);
value_info(uv[j]);
}
}
}
void vm::detail() {
reginfo();
gstate();
lstate();
ustate();
void vm::all_state_detail() {
register_info();
global_state();
local_state();
upvalue_state();
}
std::string vm::report_lack_arguments(u32 argc, const nas_func& func) const {
auto result = std::string("lack argument(s) when calling function:\n func(");
std::vector<std::string> argument_list = {};
argument_list.resize(func.keys.size());
for(const auto& i : func.keys) {
argument_list[i.second-1] = i.first;
}
for(u32 i = 0; i<argument_list.size(); ++i) {
result += argument_list[i];
if (i<argc) {
result += "[get]";
}
if (i!=argument_list.size()-1) {
result += ", ";
}
}
if (func.dynamic_parameter_index>=0) {
result += argument_list.size()? ", ":"";
result += const_string[func.dynamic_parameter_index] + "[dynamic]";
}
result += ") ";
std::stringstream out;
out << "{entry: 0x" << std::hex << func.entry << std::dec << "}";
out << " @ 0x" << std::hex << reinterpret_cast<u64>(&func) << std::dec;
return result + out.str();
}
std::string vm::report_special_call_lack_arguments(
var* local, const nas_func& func) const {
auto result = std::string("lack argument(s) when calling function:\n func(");
std::vector<std::string> argument_list = {};
argument_list.resize(func.keys.size());
for(const auto& i : func.keys) {
argument_list[i.second-1] = i.first;
}
for(const auto& key : argument_list) {
if (local[func.keys.at(key)].type==vm_none) {
result += key + ", ";
} else {
result += key + "[get], ";
}
}
result = result.substr(0, result.length()-2);
result += ") ";
std::stringstream out;
out << "{entry: 0x" << std::hex << func.entry << std::dec << "}";
out << " @ 0x" << std::hex << reinterpret_cast<u64>(&func) << std::dec;
return result + out.str();
}
std::string vm::report_key_not_found(
const std::string& not_found, const nas_hash& hash) const {
auto result = "member \"" + not_found + "\" doesn't exist in hash {";
for(const auto& i : hash.elems) {
result += i.first + ", ";
}
if (hash.elems.size()) {
result = result.substr(0, result.length()-2);
}
result += "}";
return result;
}
std::string vm::report_out_of_range(f64 index, usize real_size) const {
auto result = "index out of range: " + std::to_string(index);
result += " but max size is " + std::to_string(real_size);
if (!real_size) {
return result;
}
result += ", index range is -" + std::to_string(real_size);
result += "~" + std::to_string(real_size-1);
return result;
}
std::string vm::type_name_string(const var& value) const {
switch(value.type) {
case vm_none: return "none";
case vm_cnt: return "counter";
case vm_addr: return "address";
case vm_ret: return "program counter";
case vm_nil: return "nil";
case vm_num: return "number";
case vm_str: return "string";
case vm_vec: return "vector";
case vm_hash: return "hash";
case vm_func: return "function";
case vm_upval: return "upvalue";
case vm_obj: return "ghost type";
case vm_co: return "coroutine";
case vm_map: return "namespace";
}
return "unknown";
}
void vm::die(const std::string& str) {
std::cerr << "[vm] error: " << str << "\n";
traceback();
stackinfo();
function_call_trace();
trace_back();
stack_info();
// show verbose crash info
if (verbose) {
detail();
all_state_detail();
}
if (!ngc.cort) {
@ -244,7 +401,7 @@ void vm::die(const std::string& str) {
} else {
// in coroutine, shut down the coroutine and return to main context
ctx.pc = 0; // mark coroutine 'dead'
ngc.ctxreserve(); // switch context to main
ngc.context_reserve(); // switch context to main
ctx.top[0] = nil; // generate return value 'nil'
}
}

View File

@ -2,6 +2,8 @@
#include <iomanip>
#include <stack>
#include <cstring>
#include <sstream>
#include "nasal_import.h"
#include "nasal_gc.h"
@ -22,10 +24,10 @@ protected:
context ctx;
/* constants */
const f64* cnum = nullptr; // constant numbers
const std::string* cstr = nullptr; // constant symbols and strings
const f64* const_number = nullptr; // constant numbers
const std::string* const_string = nullptr; // constant symbols and strings
std::vector<u32> imm; // immediate number table
std::vector<nasal_builtin_table> native;
std::vector<nasal_builtin_table> native_function;
/* garbage collector */
gc ngc;
@ -57,14 +59,21 @@ protected:
/* debug functions */
bool verbose = false;
void valinfo(var&);
void traceback();
void stackinfo(const u32);
void reginfo();
void gstate();
void lstate();
void ustate();
void detail();
void value_info(var&);
void function_detail_info(const nas_func&);
void function_call_trace();
void trace_back();
void stack_info(const u32);
void register_info();
void global_state();
void local_state();
void upvalue_state();
void all_state_detail();
std::string report_lack_arguments(u32, const nas_func&) const;
std::string report_special_call_lack_arguments(var*, const nas_func&) const;
std::string report_key_not_found(const std::string&, const nas_hash&) const;
std::string report_out_of_range(f64, usize) const;
std::string type_name_string(const var&) const;
void die(const std::string&);
/* vm calculation functions*/
@ -204,7 +213,7 @@ inline void vm::o_repl() {
inline void vm::o_intl() {
ctx.top[0].func().local.resize(imm[ctx.pc], nil);
ctx.top[0].func().lsize = imm[ctx.pc];
ctx.top[0].func().local_size = imm[ctx.pc];
}
inline void vm::o_loadg() {
@ -217,11 +226,11 @@ inline void vm::o_loadl() {
inline void vm::o_loadu() {
ctx.funcr.func().upval[(imm[ctx.pc]>>16)&0xffff]
.upval()[imm[ctx.pc]&0xffff] = (ctx.top--)[0];
.upval()[imm[ctx.pc]&0xffff] = (ctx.top--)[0];
}
inline void vm::o_pnum() {
(++ctx.top)[0] = var::num(cnum[imm[ctx.pc]]);
(++ctx.top)[0] = var::num(const_number[imm[ctx.pc]]);
}
inline void vm::o_pnil() {
@ -252,7 +261,7 @@ inline void vm::o_newf() {
(++ctx.top)[0] = ngc.alloc(vm_func);
auto& func = ctx.top[0].func();
func.entry = imm[ctx.pc];
func.psize = 1;
func.parameter_size = 1;
/* this means you create a new function in local scope */
if (ctx.localr) {
@ -260,35 +269,35 @@ inline void vm::o_newf() {
// function created in the same local scope shares one closure
// so this size & stk setting has no problem
var upval = (ctx.upvalr.type==vm_nil)? ngc.alloc(vm_upval):ctx.upvalr;
upval.upval().size = ctx.funcr.func().lsize;
upval.upval().stk = ctx.localr;
upval.upval().size = ctx.funcr.func().local_size;
upval.upval().stack_frame_offset = ctx.localr;
func.upval.push_back(upval);
ctx.upvalr = upval;
}
}
inline void vm::o_happ() {
ctx.top[-1].hash().elems[cstr[imm[ctx.pc]]] = ctx.top[0];
ctx.top[-1].hash().elems[const_string[imm[ctx.pc]]] = ctx.top[0];
--ctx.top;
}
inline void vm::o_para() {
auto& func = ctx.top[0].func();
// func->size has 1 place reserved for "me"
func.keys[imm[ctx.pc]] = func.psize;
func.local[func.psize++] = var::none();
func.keys[const_string[imm[ctx.pc]]] = func.parameter_size;
func.local[func.parameter_size++] = var::none();
}
inline void vm::o_deft() {
var val = ctx.top[0];
auto& func = (--ctx.top)[0].func();
// func->size has 1 place reserved for "me"
func.keys[imm[ctx.pc]] = func.psize;
func.local[func.psize++] = val;
func.keys[const_string[imm[ctx.pc]]] = func.parameter_size;
func.local[func.parameter_size++] = val;
}
inline void vm::o_dyn() {
ctx.top[0].func().dpara = imm[ctx.pc];
ctx.top[0].func().dynamic_parameter_index = imm[ctx.pc];
}
inline void vm::o_lnot() {
@ -304,12 +313,14 @@ inline void vm::o_lnot() {
ctx.top[0] = num? zero:one;
}
} break;
default: die("incorrect value type"); return;
default:
die("cannot do not-operation on "+type_name_string(val));
return;
}
}
inline void vm::o_usub() {
ctx.top[0] = var::num(-ctx.top[0].tonum());
ctx.top[0] = var::num(-ctx.top[0].to_num());
}
inline void vm::o_bnot() {
@ -318,30 +329,30 @@ inline void vm::o_bnot() {
inline void vm::o_btor() {
ctx.top[-1] = var::num(
static_cast<i32>(ctx.top[-1].tonum())|
static_cast<i32>(ctx.top[0].tonum())
static_cast<i32>(ctx.top[-1].to_num())|
static_cast<i32>(ctx.top[0].to_num())
);
--ctx.top;
}
inline void vm::o_btxor() {
ctx.top[-1] = var::num(
static_cast<i32>(ctx.top[-1].tonum())^
static_cast<i32>(ctx.top[0].tonum())
static_cast<i32>(ctx.top[-1].to_num())^
static_cast<i32>(ctx.top[0].to_num())
);
--ctx.top;
}
inline void vm::o_btand() {
ctx.top[-1] = var::num(
static_cast<i32>(ctx.top[-1].tonum())&
static_cast<i32>(ctx.top[0].tonum())
static_cast<i32>(ctx.top[-1].to_num())&
static_cast<i32>(ctx.top[0].to_num())
);
--ctx.top;
}
#define op_calc(type)\
ctx.top[-1] = var::num(ctx.top[-1].tonum() type ctx.top[0].tonum());\
ctx.top[-1] = var::num(ctx.top[-1].to_num() type ctx.top[0].to_num());\
--ctx.top;
inline void vm::o_add() {op_calc(+);}
@ -349,6 +360,7 @@ inline void vm::o_sub() {op_calc(-);}
inline void vm::o_mul() {op_calc(*);}
inline void vm::o_div() {op_calc(/);}
inline void vm::o_lnk() {
// concat two vectors into one
if (ctx.top[-1].type==vm_vec && ctx.top[0].type==vm_vec) {
ngc.temp = ngc.alloc(vm_vec);
for(auto i : ctx.top[-1].vec().elems) {
@ -362,19 +374,20 @@ inline void vm::o_lnk() {
--ctx.top;
return;
}
ctx.top[-1] = ngc.newstr(ctx.top[-1].tostr()+ctx.top[0].tostr());
// concat strings
ctx.top[-1] = ngc.newstr(ctx.top[-1].to_str()+ctx.top[0].to_str());
--ctx.top;
}
#define op_calc_const(type)\
ctx.top[0] = var::num(ctx.top[0].tonum() type cnum[imm[ctx.pc]]);
ctx.top[0] = var::num(ctx.top[0].to_num() type const_number[imm[ctx.pc]]);
inline void vm::o_addc() {op_calc_const(+);}
inline void vm::o_subc() {op_calc_const(-);}
inline void vm::o_mulc() {op_calc_const(*);}
inline void vm::o_divc() {op_calc_const(/);}
inline void vm::o_lnkc() {
ctx.top[0] = ngc.newstr(ctx.top[0].tostr()+cstr[imm[ctx.pc]]);
ctx.top[0] = ngc.newstr(ctx.top[0].to_str()+const_string[imm[ctx.pc]]);
}
// top[0] stores the value of memr[0], to avoid being garbage-collected
@ -383,7 +396,9 @@ inline void vm::o_lnkc() {
// like this: func{a+=c;}(); the result of 'a+c' will no be used later, imm[pc] = 1
// but if b+=a+=c; the result of 'a+c' will be used later, imm[pc] = 0
#define op_calc_eq(type)\
ctx.top[-1] = ctx.memr[0] = var::num(ctx.memr[0].tonum() type ctx.top[-1].tonum());\
ctx.top[-1] = ctx.memr[0] = var::num(\
ctx.memr[0].to_num() type ctx.top[-1].to_num()\
);\
ctx.memr = nullptr;\
ctx.top -= imm[ctx.pc]+1;
@ -393,7 +408,7 @@ inline void vm::o_muleq() {op_calc_eq(*);}
inline void vm::o_diveq() {op_calc_eq(/);}
inline void vm::o_lnkeq() {
ctx.top[-1] = ctx.memr[0] = ngc.newstr(
ctx.memr[0].tostr()+ctx.top[-1].tostr()
ctx.memr[0].to_str()+ctx.top[-1].to_str()
);
ctx.memr = nullptr;
ctx.top -= imm[ctx.pc]+1;
@ -401,8 +416,8 @@ inline void vm::o_lnkeq() {
inline void vm::o_bandeq() {
ctx.top[-1] = ctx.memr[0] = var::num(
static_cast<i32>(ctx.memr[0].tonum())&
static_cast<i32>(ctx.top[-1].tonum())
static_cast<i32>(ctx.memr[0].to_num())&
static_cast<i32>(ctx.top[-1].to_num())
);
ctx.memr = nullptr;
ctx.top -= imm[ctx.pc]+1;
@ -410,8 +425,8 @@ inline void vm::o_bandeq() {
inline void vm::o_boreq() {
ctx.top[-1] = ctx.memr[0] = var::num(
static_cast<i32>(ctx.memr[0].tonum())|
static_cast<i32>(ctx.top[-1].tonum())
static_cast<i32>(ctx.memr[0].to_num())|
static_cast<i32>(ctx.top[-1].to_num())
);
ctx.memr = nullptr;
ctx.top -= imm[ctx.pc]+1;
@ -419,8 +434,8 @@ inline void vm::o_boreq() {
inline void vm::o_bxoreq() {
ctx.top[-1] = ctx.memr[0] = var::num(
static_cast<i32>(ctx.memr[0].tonum())^
static_cast<i32>(ctx.top[-1].tonum())
static_cast<i32>(ctx.memr[0].to_num())^
static_cast<i32>(ctx.top[-1].to_num())
);
ctx.memr = nullptr;
ctx.top -= imm[ctx.pc]+1;
@ -432,7 +447,9 @@ inline void vm::o_bxoreq() {
// like this: func{a+=1;}(); the result of 'a+1' will no be used later, imm[pc]>>31=1
// but if b+=a+=1; the result of 'a+1' will be used later, imm[pc]>>31=0
#define op_calc_eq_const(type)\
ctx.top[0] = ctx.memr[0] = var::num(ctx.memr[0].tonum() type cnum[imm[ctx.pc]]);\
ctx.top[0] = ctx.memr[0] = var::num(\
ctx.memr[0].to_num() type const_number[imm[ctx.pc]]\
);\
ctx.memr = nullptr;
inline void vm::o_addeqc() {op_calc_eq_const(+);}
@ -440,12 +457,16 @@ inline void vm::o_subeqc() {op_calc_eq_const(-);}
inline void vm::o_muleqc() {op_calc_eq_const(*);}
inline void vm::o_diveqc() {op_calc_eq_const(/);}
inline void vm::o_lnkeqc() {
ctx.top[0] = ctx.memr[0] = ngc.newstr(ctx.memr[0].tostr()+cstr[imm[ctx.pc]]);
ctx.top[0] = ctx.memr[0] = ngc.newstr(
ctx.memr[0].to_str()+const_string[imm[ctx.pc]]
);
ctx.memr = nullptr;
}
#define op_calc_eq_const_and_pop(type)\
ctx.top[0] = ctx.memr[0] = var::num(ctx.memr[0].tonum() type cnum[imm[ctx.pc]]);\
ctx.top[0] = ctx.memr[0] = var::num(\
ctx.memr[0].to_num() type const_number[imm[ctx.pc]]\
);\
ctx.memr = nullptr;\
--ctx.top;
@ -455,7 +476,7 @@ inline void vm::o_mulecp() {op_calc_eq_const_and_pop(*);}
inline void vm::o_divecp() {op_calc_eq_const_and_pop(/);}
inline void vm::o_lnkecp() {
ctx.top[0] = ctx.memr[0] = ngc.newstr(
ctx.memr[0].tostr()+cstr[imm[ctx.pc]]
ctx.memr[0].to_str()+const_string[imm[ctx.pc]]
);
ctx.memr = nullptr;
--ctx.top;
@ -481,7 +502,7 @@ inline void vm::o_eq() {
ctx.top[0] = (val1.str()==val2.str())? one:zero;
} else if ((val1.type==vm_num || val2.type==vm_num)
&& val1.type!=vm_nil && val2.type!=vm_nil) {
ctx.top[0] = (val1.tonum()==val2.tonum())? one:zero;
ctx.top[0] = (val1.to_num()==val2.to_num())? one:zero;
} else {
ctx.top[0] = (val1==val2)? one:zero;
}
@ -496,7 +517,7 @@ inline void vm::o_neq() {
ctx.top[0] = (val1.str()!=val2.str())? one:zero;
} else if ((val1.type==vm_num || val2.type==vm_num)
&& val1.type!=vm_nil && val2.type!=vm_nil) {
ctx.top[0] = (val1.tonum()!=val2.tonum())? one:zero;
ctx.top[0] = (val1.to_num()!=val2.to_num())? one:zero;
} else {
ctx.top[0] = (val1!=val2)? one:zero;
}
@ -504,7 +525,7 @@ inline void vm::o_neq() {
#define op_cmp(type)\
--ctx.top;\
ctx.top[0] = (ctx.top[0].tonum() type ctx.top[1].tonum())?one:zero;
ctx.top[0] = (ctx.top[0].to_num() type ctx.top[1].to_num())? one:zero;
inline void vm::o_less() {op_cmp(<);}
inline void vm::o_leq() {op_cmp(<=);}
@ -512,7 +533,7 @@ inline void vm::o_grt() {op_cmp(>);}
inline void vm::o_geq() {op_cmp(>=);}
#define op_cmp_const(type)\
ctx.top[0] = (ctx.top[0].tonum() type cnum[imm[ctx.pc]])?one:zero;
ctx.top[0] = (ctx.top[0].to_num() type const_number[imm[ctx.pc]])? one:zero;
inline void vm::o_lessc() {op_cmp_const(<);}
inline void vm::o_leqc() {op_cmp_const(<=);}
@ -545,7 +566,9 @@ inline void vm::o_jf() {
inline void vm::o_cnt() {
if (ctx.top[0].type!=vm_vec) {
die("must use vector in forindex/foreach");
die("must use vector in forindex/foreach but get "+
type_name_string(ctx.top[0])
);
return;
}
(++ctx.top)[0] = var::cnt(-1);
@ -589,29 +612,29 @@ inline void vm::o_callv() {
var val = ctx.top[0];
var vec = (--ctx.top)[0];
if (vec.type==vm_vec) {
ctx.top[0] = vec.vec().get_val(val.tonum());
ctx.top[0] = vec.vec().get_value(val.to_num());
if (ctx.top[0].type==vm_none) {
die("out of range:"+std::to_string(val.tonum()));
die(report_out_of_range(val.to_num(), vec.vec().size()));
return;
}
} else if (vec.type==vm_hash) {
if (val.type!=vm_str) {
die("must use string as the key");
die("must use string as the key but get "+type_name_string(val));
return;
}
ctx.top[0] = vec.hash().get_val(val.str());
ctx.top[0] = vec.hash().get_value(val.str());
if (ctx.top[0].type==vm_none) {
die("cannot find member \""+val.str()+"\"");
die(report_key_not_found(val.str(), vec.hash()));
return;
} else if (ctx.top[0].type==vm_func) {
ctx.top[0].func().local[0] = val; // 'me'
}
} else if (vec.type==vm_str) {
auto& str = vec.str();
i32 num = val.tonum();
const auto& str = vec.str();
i32 num = val.to_num();
i32 len = str.length();
if (num<-len || num>=len) {
die("out of range:"+std::to_string(val.tonum()));
die(report_out_of_range(num, str.size()));
return;
}
ctx.top[0] = var::num(
@ -619,16 +642,16 @@ inline void vm::o_callv() {
);
} else if (vec.type==vm_map) {
if (val.type!=vm_str) {
die("must use string as the key");
die("must use string as the key but get "+type_name_string(val));
return;
}
ctx.top[0] = vec.map().get_val(val.str());
ctx.top[0] = vec.map().get_value(val.str());
if (ctx.top[0].type==vm_none) {
die("cannot find symbol \""+val.str()+"\"");
return;
}
} else {
die("must call a vector/hash/string");
die("must call a vector/hash/string but get "+type_name_string(vec));
return;
}
}
@ -636,13 +659,13 @@ inline void vm::o_callv() {
inline void vm::o_callvi() {
var val = ctx.top[0];
if (val.type!=vm_vec) {
die("must use a vector");
die("must use a vector but get "+type_name_string(val));
return;
}
// cannot use operator[],because this may cause overflow
(++ctx.top)[0] = val.vec().get_val(imm[ctx.pc]);
(++ctx.top)[0] = val.vec().get_value(imm[ctx.pc]);
if (ctx.top[0].type==vm_none) {
die("out of range:"+std::to_string(imm[ctx.pc]));
die(report_out_of_range(imm[ctx.pc], val.vec().size()));
return;
}
}
@ -650,18 +673,18 @@ inline void vm::o_callvi() {
inline void vm::o_callh() {
var val = ctx.top[0];
if (val.type!=vm_hash && val.type!=vm_map) {
die("must call a hash");
die("must call a hash but get "+type_name_string(val));
return;
}
const auto& str = cstr[imm[ctx.pc]];
const auto& str = const_string[imm[ctx.pc]];
if (val.type==vm_hash) {
ctx.top[0] = val.hash().get_val(str);
ctx.top[0] = val.hash().get_value(str);
} else {
ctx.top[0] = val.map().get_val(str);
ctx.top[0] = val.map().get_value(str);
}
if (ctx.top[0].type==vm_none) {
val.type==vm_hash?
die("member \"" + str + "\" does not exist"):
die(report_key_not_found(str, val.hash())):
die("cannot find symbol \"" + str + "\"");
return;
} else if (ctx.top[0].type==vm_func) {
@ -670,13 +693,13 @@ inline void vm::o_callh() {
}
inline void vm::o_callfv() {
u32 argc = imm[ctx.pc]; // arguments counter
const u32 argc = imm[ctx.pc]; // arguments counter
var* local = ctx.top-argc+1; // arguments begin address
if (local[-1].type!=vm_func) {
die("must call a function");
die("must call a function but get "+type_name_string(local[-1]));
return;
}
auto& func = local[-1].func();
const auto& func = local[-1].func();
// swap funcr with local[-1]
var tmp = local[-1];
@ -684,27 +707,29 @@ inline void vm::o_callfv() {
ctx.funcr = tmp;
// top-argc+lsize(local) +1(old pc) +1(old localr) +1(old upvalr)
if (ctx.top-argc+func.lsize+3>=ctx.canary) {
if (ctx.top-argc+func.local_size+3>=ctx.canary) {
die("stack overflow");
return;
}
// parameter size is func->psize-1, 1 is reserved for "me"
u32 psize = func.psize-1;
if (argc<psize && func.local[argc+1].type==vm_none) {
die("lack argument(s)");
const u32 parameter_size = func.parameter_size-1;
if (argc<parameter_size && func.local[argc+1].type==vm_none) {
die(report_lack_arguments(argc, func));
return;
}
// load dynamic argument, default nil, for better performance
var dynamic = nil;
if (func.dpara>=0) { // load dynamic arguments
if (func.dynamic_parameter_index>=0) {
// load dynamic argument
dynamic = ngc.alloc(vm_vec);
for(u32 i = psize; i<argc; ++i) {
for(u32 i = parameter_size; i<argc; ++i) {
dynamic.vec().elems.push_back(local[i]);
}
} else if (psize<argc) {
// load arguments to "arg", located at stack+1
} else if (parameter_size<argc) {
// load arguments to default dynamic argument "arg", located at stack+1
dynamic = ngc.alloc(vm_vec);
for(u32 i = psize; i<argc; ++i) {
for(u32 i = parameter_size; i<argc; ++i) {
dynamic.vec().elems.push_back(local[i]);
}
}
@ -713,19 +738,21 @@ inline void vm::o_callfv() {
// then all the available values the vector needs
// are all outside the stack top and may be
// collected incorrectly
ctx.top = local+func.lsize;
ctx.top = local+func.local_size;
u32 min_size = (std::min)(psize, argc); // avoid error in MSVC
const u32 min_size = (std::min)(parameter_size, argc); // avoid error in MSVC
for(u32 i = min_size; i>=1; --i) { // load arguments
local[i] = local[i-1];
}
local[0] = func.local[0];// load "me"
// load local scope & default arguments
for(u32 i = min_size+1; i<func.lsize; ++i) {
for(u32 i = min_size+1; i<func.local_size; ++i) {
local[i] = func.local[i];
}
local[func.dpara>=0? psize+1:func.lsize-1] = dynamic;
// load dynamic argument
local[func.dynamic_parameter_index>=0?
parameter_size+1:func.local_size-1] = dynamic;
ctx.top[0] = ctx.upvalr;
(++ctx.top)[0] = var::addr(ctx.localr);
@ -736,41 +763,48 @@ inline void vm::o_callfv() {
}
inline void vm::o_callfh() {
auto& hash = ctx.top[0].hash().elems;
const auto& hash = ctx.top[0].hash().elems;
if (ctx.top[-1].type!=vm_func) {
die("must call a function");
die("must call a function but get "+type_name_string(ctx.top[-1]));
return;
}
auto& func = ctx.top[-1].func();
const auto& func = ctx.top[-1].func();
var tmp = ctx.top[-1];
ctx.top[-1] = ctx.funcr;
ctx.funcr = tmp;
// top -1(hash) +lsize(local) +1(old pc) +1(old localr) +1(old upvalr)
if (ctx.top+func.lsize+2>= ctx.canary) {
if (ctx.top+func.local_size+2>= ctx.canary) {
die("stack overflow");
return;
}
if (func.dpara>=0) {
die("special call cannot use dynamic argument");
// dynamic parameter is not allowed in this kind of function call
if (func.dynamic_parameter_index>=0) {
die("special call cannot use dynamic argument \"" +
const_string[func.dynamic_parameter_index] + "\""
);
return;
}
var* local = ctx.top;
ctx.top += func.lsize;
for(u32 i = 0; i<func.lsize; ++i) {
ctx.top += func.local_size;
for(u32 i = 0; i<func.local_size; ++i) {
local[i] = func.local[i];
}
bool lack_arguments_flag = false;
for(const auto& i : func.keys) {
auto& key = cstr[i.first];
const auto& key = i.first;
if (hash.count(key)) {
local[i.second] = hash[key];
local[i.second] = hash.at(key);
} else if (local[i.second].type==vm_none) {
die("lack argument(s): \""+key+"\"");
return;
lack_arguments_flag = true;
}
}
if (lack_arguments_flag) {
die(report_special_call_lack_arguments(local, func));
return;
}
ctx.top[0] = ctx.upvalr;
(++ctx.top)[0] = var::addr(ctx.localr);
@ -787,11 +821,12 @@ inline void vm::o_callb() {
// if running a native function about coroutine
// (top) will be set to another context.top, instead of main_context.top
var tmp = (*native[imm[ctx.pc]].func)(ctx.localr, ngc);
auto function_pointer = native_function[imm[ctx.pc]].func;
var result = (*function_pointer)(&ctx, &ngc);
// so we use tmp variable to store this return value
// and set it to top[0] later
ctx.top[0] = tmp;
ctx.top[0] = result;
// if get none, this means errors occurred when calling this native function
if (ctx.top[0].type==vm_none) {
@ -808,7 +843,7 @@ inline void vm::o_slcbeg() {
// +--------------+
(++ctx.top)[0] = ngc.alloc(vm_vec);
if (ctx.top[-1].type!=vm_vec) {
die("must slice a vector");
die("must slice a vector but get "+type_name_string(ctx.top[-1]));
return;
}
}
@ -820,9 +855,9 @@ inline void vm::o_slcend() {
inline void vm::o_slc() {
var val = (ctx.top--)[0];
var res = ctx.top[-1].vec().get_val(val.tonum());
var res = ctx.top[-1].vec().get_value(val.to_num());
if (res.type==vm_none) {
die("index " + std::to_string(val.tonum()) + " out of range");
die(report_out_of_range(val.to_num(), ctx.top[-1].vec().size()));
return;
}
ctx.top[0].vec().elems.push_back(res);
@ -831,12 +866,12 @@ inline void vm::o_slc() {
inline void vm::o_slc2() {
var val2 = (ctx.top--)[0];
var val1 = (ctx.top--)[0];
auto& ref = ctx.top[-1].vec().elems;
const auto& ref = ctx.top[-1].vec().elems;
auto& aim = ctx.top[0].vec().elems;
u8 type1 = val1.type,type2=val2.type;
i32 num1 = val1.tonum();
i32 num2 = val2.tonum();
i32 num1 = val1.to_num();
i32 num2 = val2.to_num();
i32 size = ref.size();
if (type1==vm_nil && type2==vm_nil) {
num1 = 0;
@ -849,7 +884,9 @@ inline void vm::o_slc2() {
if (num1<-size || num1>=size || num2<-size || num2>=size) {
die("index " + std::to_string(num1) + ":" +
std::to_string(num2) + " out of range");
std::to_string(num2) + " out of range, real size is " +
std::to_string(size)
);
return;
} else if (num1<=num2) {
for(i32 i = num1; i<=num2; ++i) {
@ -875,8 +912,9 @@ inline void vm::o_mcalll() {
inline void vm::o_mupval() {
ctx.memr = &(
ctx.funcr.func()
.upval[(imm[ctx.pc]>>16)&0xffff]
.upval()[imm[ctx.pc]&0xffff]);
.upval[(imm[ctx.pc]>>16)&0xffff]
.upval()[imm[ctx.pc]&0xffff]
);
(++ctx.top)[0] = ctx.memr[0];
// push value in this memory space on stack
// to avoid being garbage collected
@ -886,31 +924,31 @@ inline void vm::o_mcallv() {
var val = ctx.top[0]; // index
var vec = (--ctx.top)[0]; // mcall vector, reserved on stack to avoid gc
if (vec.type==vm_vec) {
ctx.memr = vec.vec().get_mem(val.tonum());
ctx.memr = vec.vec().get_memory(val.to_num());
if (!ctx.memr) {
die("index "+std::to_string(val.tonum())+" out of range");
die(report_out_of_range(val.to_num(), vec.vec().size()));
return;
}
} else if (vec.type==vm_hash) { // do mcallh but use the mcallv way
if (val.type!=vm_str) {
die("key must be string");
die("must use string as the key but get "+type_name_string(val));
return;
}
auto& ref = vec.hash();
auto& str = val.str();
ctx.memr = ref.get_mem(str);
const auto& str = val.str();
ctx.memr = ref.get_memory(str);
if (!ctx.memr) {
ref.elems[str] = nil;
ctx.memr = ref.get_mem(str);
ctx.memr = ref.get_memory(str);
}
} else if (vec.type==vm_map) {
if (val.type!=vm_str) {
die("key must be string");
die("must use string as the key but get "+type_name_string(val));
return;
}
auto& ref = vec.map();
auto& str = val.str();
ctx.memr = ref.get_mem(str);
const auto& str = val.str();
ctx.memr = ref.get_memory(str);
if (!ctx.memr) {
die("cannot find symbol \"" + str + "\"");
}
@ -923,22 +961,23 @@ inline void vm::o_mcallv() {
inline void vm::o_mcallh() {
var hash = ctx.top[0]; // mcall hash, reserved on stack to avoid gc
if (hash.type!=vm_hash && hash.type!=vm_map) {
die("must call a hash");
die("must call a hash/namespace but get "+type_name_string(hash));
return;
}
auto& str = cstr[imm[ctx.pc]];
const auto& str = const_string[imm[ctx.pc]];
if (hash.type==vm_map) {
ctx.memr = hash.map().get_mem(str);
ctx.memr = hash.map().get_memory(str);
if (!ctx.memr) {
die("cannot find symbol \"" + str + "\"");
}
return;
}
auto& ref = hash.hash();
ctx.memr = ref.get_mem(str);
if (!ctx.memr) { // create a new key
ctx.memr = ref.get_memory(str);
// create a new key
if (!ctx.memr) {
ref.elems[str] = nil;
ctx.memr = ref.get_mem(str);
ctx.memr = ref.get_memory(str);
}
}
@ -970,10 +1009,11 @@ inline void vm::o_ret() {
ctx.funcr = ctx.top[0];
ctx.top[0] = ret; // rewrite func with returned value
if (up.type==vm_upval) { // synchronize upvalue
// synchronize upvalue
if (up.type==vm_upval) {
auto& upval = up.upval();
auto size = func.func().lsize;
upval.onstk = false;
auto size = func.func().local_size;
upval.on_stack = false;
upval.elems.resize(size);
for(u32 i = 0; i<size; ++i) {
upval.elems[i] = local[i];
@ -984,7 +1024,7 @@ inline void vm::o_ret() {
// because there maybe another function call inside but return here
// coroutine function ends with setting pc to 0
if (!ctx.pc) {
ngc.ctxreserve();
ngc.context_reserve();
}
}

View File

@ -7,7 +7,7 @@
namespace nasal {
class optimizer:public ast_visitor {
class optimizer: public ast_visitor {
private:
void const_string(binary_operator*, string_literal*, string_literal*);
void const_number(binary_operator*, number_literal*, number_literal*);

View File

@ -111,7 +111,10 @@ void repl::execute() {
info::instance()->in_repl_mode = true;
std::cout << "[nasal-repl] Initializating enviroment...\n";
// run on pass for initializing basic modules, without output
run();
if (!run()) {
std::cout << "[nasal-repl] Initialization failed.\n\n";
std::exit(-1);
}
// allow output now
runtime.set_allow_repl_output_flag(true);
std::cout << "[nasal-repl] Initialization complete.\n\n";

View File

@ -29,7 +29,7 @@ bool symbol_finder::visit_function(function* node) {
}
bool symbol_finder::visit_iter_expr(iter_expr* node) {
if (node->get_name()) {
if (node->is_definition() && node->get_name()) {
symbols.push_back({
node->get_name()->get_name(),
node->get_name()->get_location()

View File

@ -9,7 +9,7 @@
namespace nasal {
class symbol_finder:public ast_visitor {
class symbol_finder: public ast_visitor {
public:
struct symbol_info {
std::string name;

View File

@ -12,130 +12,130 @@ void dir_entry_destructor(void* ptr) {
#endif
}
var builtin_pipe(var* local, gc& ngc) {
var builtin_pipe(context* ctx, gc* ngc) {
#ifndef _WIN32
i32 fd[2];
var res = ngc.alloc(vm_vec);
var res = ngc->alloc(vm_vec);
if (pipe(fd)==-1) {
return nas_err("pipe", "failed to create pipe");
return nas_err("unix::pipe", "failed to create pipe");
}
res.vec().elems.push_back(var::num(static_cast<f64>(fd[0])));
res.vec().elems.push_back(var::num(static_cast<f64>(fd[1])));
return res;
#endif
return nas_err("pipe", "not supported on windows");
return nas_err("unix::pipe", "not supported on windows");
}
var builtin_fork(var* local, gc& ngc) {
var builtin_fork(context* ctx, gc* ngc) {
#ifndef _WIN32
f64 res=fork();
if (res<0) {
return nas_err("fork", "failed to fork a process");
return nas_err("unix::fork", "failed to fork a process");
}
return var::num(static_cast<f64>(res));
#endif
return nas_err("fork", "not supported on windows");
return nas_err("unix::fork", "not supported on windows");
}
var builtin_waitpid(var* local, gc& ngc) {
var pid = local[1];
var nohang = local[2];
var builtin_waitpid(context* ctx, gc* ngc) {
auto pid = ctx->localr[1];
auto nohang = ctx->localr[2];
if (pid.type!=vm_num || nohang.type!=vm_num) {
return nas_err("waitpid", "pid and nohang must be number");
return nas_err("unix::waitpid", "pid and nohang must be number");
}
#ifndef _WIN32
i32 ret_pid, status;
ret_pid = waitpid(pid.num(), &status, nohang.num()==0? 0:WNOHANG);
var vec = ngc.alloc(vm_vec);
var vec = ngc->alloc(vm_vec);
vec.vec().elems.push_back(var::num(static_cast<f64>(ret_pid)));
vec.vec().elems.push_back(var::num(static_cast<f64>(status)));
return vec;
#endif
return nas_err("waitpid", "not supported on windows");
return nas_err("unix::waitpid", "not supported on windows");
}
var builtin_opendir(var* local, gc& ngc) {
var path = local[1];
var builtin_opendir(context* ctx, gc* ngc) {
auto path = ctx->localr[1];
if (path.type!=vm_str) {
return nas_err("opendir", "\"path\" must be string");
return nas_err("unix::opendir", "\"path\" must be string");
}
#ifdef _MSC_VER
WIN32_FIND_DATAA data;
HANDLE p;
p = FindFirstFileA((path.str()+"\\*.*").c_str(), &data);
if (p==INVALID_HANDLE_VALUE) {
return nas_err("opendir", "cannot open dir <"+path.str()+">");
return nas_err("unix::opendir", "cannot open dir <"+path.str()+">");
}
#else
DIR* p = opendir(path.str().c_str());
if (!p) {
return nas_err("opendir", "cannot open dir <"+path.str()+">");
return nas_err("unix::opendir", "cannot open dir <"+path.str()+">");
}
#endif
var ret = ngc.alloc(vm_obj);
ret.obj().set(dir_type_name, dir_entry_destructor, p);
var ret = ngc->alloc(vm_obj);
ret.ghost().set(dir_type_name, dir_entry_destructor, p);
return ret;
}
var builtin_readdir(var* local, gc& ngc) {
var handle = local[1];
if (!handle.objchk(dir_type_name)) {
return nas_err("readdir", "not a valid dir handle");
var builtin_readdir(context* ctx, gc* ngc) {
auto handle = ctx->localr[1];
if (!handle.object_check(dir_type_name)) {
return nas_err("unix::readdir", "not a valid dir handle");
}
#ifdef _MSC_VER
WIN32_FIND_DATAA data;
if (!FindNextFileA(handle.obj().ptr,&data)) {
if (!FindNextFileA(handle.ghost().pointer, &data)) {
return nil;
}
return ngc.newstr(data.cFileName);
return ngc->newstr(data.cFileName);
#else
dirent* p = readdir(static_cast<DIR*>(handle.obj().ptr));
return p? ngc.newstr(p->d_name):nil;
dirent* p = readdir(static_cast<DIR*>(handle.ghost().pointer));
return p? ngc->newstr(p->d_name):nil;
#endif
}
var builtin_closedir(var* local, gc& ngc) {
var handle = local[1];
if (!handle.objchk(dir_type_name)) {
return nas_err("closedir", "not a valid dir handle");
var builtin_closedir(context* ctx, gc* ngc) {
auto handle = ctx->localr[1];
if (!handle.object_check(dir_type_name)) {
return nas_err("unix::closedir", "not a valid dir handle");
}
handle.obj().clear();
handle.ghost().clear();
return nil;
}
var builtin_chdir(var* local, gc& ngc) {
var path = local[1];
var builtin_chdir(context* ctx, gc* ngc) {
auto path = ctx->localr[1];
if (path.type!=vm_str) {
return var::num(-1.0);
}
return var::num(static_cast<f64>(chdir(path.str().c_str())));
}
var builtin_environ(var* local, gc& ngc) {
var res = ngc.temp = ngc.alloc(vm_vec);
var builtin_environ(context* ctx, gc* ngc) {
var res = ngc->temp = ngc->alloc(vm_vec);
auto& vec = res.vec().elems;
for(char** env = environ; *env; ++env) {
vec.push_back(ngc.newstr(*env));
vec.push_back(ngc->newstr(*env));
}
ngc.temp = nil;
ngc->temp = nil;
return res;
}
var builtin_getcwd(var* local, gc& ngc) {
var builtin_getcwd(context* ctx, gc* ngc) {
char buf[1024];
if (!getcwd(buf, sizeof(buf))) {
return nil;
}
return ngc.newstr(buf);
return ngc->newstr(buf);
}
var builtin_getenv(var* local, gc& ngc) {
var envvar = local[1];
var builtin_getenv(context* ctx, gc* ngc) {
auto envvar = ctx->localr[1];
if (envvar.type!=vm_str) {
return nas_err("getenv", "\"envvar\" must be string");
return nas_err("unix::getenv", "\"envvar\" must be string");
}
char* res = getenv(envvar.str().c_str());
return res? ngc.newstr(res):nil;
return res? ngc->newstr(res):nil;
}
nasal_builtin_table unix_lib_native[] = {

View File

@ -24,16 +24,16 @@ namespace nasal {
void dir_entry_destructor(void*);
var builtin_pipe(var*, gc&);
var builtin_fork(var*, gc&);
var builtin_waitpid(var*, gc&);
var builtin_opendir(var*, gc&);
var builtin_readdir(var*, gc&);
var builtin_closedir(var*, gc&);
var builtin_chdir(var*, gc&);
var builtin_environ(var*, gc&);
var builtin_getcwd(var*, gc&);
var builtin_getenv(var*, gc&);
var builtin_pipe(context*, gc*);
var builtin_fork(context*, gc*);
var builtin_waitpid(context*, gc*);
var builtin_opendir(context*, gc*);
var builtin_readdir(context*, gc*);
var builtin_closedir(context*, gc*);
var builtin_chdir(context*, gc*);
var builtin_environ(context*, gc*);
var builtin_getcwd(context*, gc*);
var builtin_getenv(context*, gc*);
extern nasal_builtin_table unix_lib_native[];

View File

@ -2,8 +2,16 @@
# 2021 ValKmjolnir
var (
_j_eof, _j_lbrace, _j_rbrace, _j_lbrkt, _j_rbrkt,
_j_comma, _j_colon, _j_str, _j_num, _j_id
_j_eof,
_j_lbrace,
_j_rbrace,
_j_lbrkt,
_j_rbrkt,
_j_comma,
_j_colon,
_j_str,
_j_num,
_j_id
) = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
var _j_content = [
@ -19,7 +27,7 @@ var _j_content = [
"identifier"
];
var JSON = func() {
var parse = func() {
var text = "";
var line = 1;
@ -66,7 +74,7 @@ var JSON = func() {
var get = func(str) {
init();
if (!size(str)) {
println("JSON.parse: empty string");
println("json::parse: empty string");
str = "[]";
}
text = str;
@ -146,28 +154,29 @@ var JSON = func() {
}
var match = func(type) {
if(token.type!=type)
println("JSON.parse: line ",line,": expect ",_j_content[type]," but get `",token.content,"`.");
if(token.type!=type) {
println("json::parse: line ",line,": expect ",_j_content[type]," but get `",token.content,"`.");
}
next();
return;
}
var member = func(hash) {
var name = token.content;
if(token.type==_j_rbrace) {
if (token.type==_j_rbrace) {
return;
}
if(token.type==_j_str) {
if (token.type==_j_str) {
match(_j_str);
} else {
match(_j_id);
}
match(_j_colon);
if(token.type==_j_lbrace) {
if (token.type==_j_lbrace) {
hash[name] = hash_gen();
} elsif(token.type==_j_lbrkt) {
} elsif (token.type==_j_lbrkt) {
hash[name] = vec_gen();
} elsif(token.type==_j_str or token.type==_j_num) {
} elsif (token.type==_j_str or token.type==_j_num) {
hash[name] = token.content;
next();
}
@ -189,21 +198,21 @@ var JSON = func() {
var vec_gen = func() {
var vec = [];
match(_j_lbrkt);
if(token.type==_j_lbrace) {
if (token.type==_j_lbrace) {
append(vec, hash_gen());
} elsif(token.type==_j_lbrkt) {
} elsif (token.type==_j_lbrkt) {
append(vec, vec_gen());
} elsif(token.type==_j_str or token.type==_j_num) {
} elsif (token.type==_j_str or token.type==_j_num) {
append(vec, token.content);
next();
}
while(token.type==_j_comma) {
match(_j_comma);
if(token.type==_j_lbrace) {
if (token.type==_j_lbrace) {
append(vec, hash_gen());
} elsif(token.type==_j_lbrkt) {
} elsif (token.type==_j_lbrkt) {
append(vec, vec_gen());
} elsif(token.type==_j_str or token.type==_j_num) {
} elsif (token.type==_j_str or token.type==_j_num) {
append(vec, token.content);
next();
}
@ -212,43 +221,43 @@ var JSON = func() {
return vec;
}
return {
parse:func(str) {
if(typeof(str)!="str") {
println("JSON.parse: must use string");
return [];
}
get(str);
next();
if (token.type==_j_lbrkt) {
var res = vec_gen();
} else {
var res = hash_gen();
}
init();
return res;
return func(source) {
if(typeof(source)!="str") {
println("json::parse: must use string but get", typeof(str));
return [];
}
};
get(source);
next();
if (token.type==_j_lbrkt) {
var res = vec_gen();
} else {
var res = hash_gen();
}
init();
return res;
}
}();
JSON.stringify = func(object) {
if(typeof(object)!="hash" and typeof(object)!="vec") {
println("JSON.stringify: must use hashmap or vector");
var stringify = func(object) {
var object_type = typeof(object);
if(object_type!="hash" and object_type!="vec" and object_type!="namespace") {
println("json::stringify: must use hashmap or vector, but get ", typeof(object));
return "[]";
}
var s = "";
var gen = func(elem) {
var t = typeof(elem);
if(t=="num") {
if (t=="num") {
s ~= str(elem);
} elsif(t=="str") {
} elsif (t=="str") {
s ~= '"'~elem~'"';
} elsif(t=="vec") {
} elsif (t=="vec") {
vgen(elem);
} elsif(t=="hash") {
} elsif (t=="hash") {
hgen(elem);
} else {
s ~= '"undefined"';

View File

@ -75,6 +75,10 @@ var floor = func(val) {
return __floor(val);
}
var ceil = func(val) {
return __ceil(val);
}
# exit using std::exit
var exit = func(val = -1) {
return __exit(val);
@ -228,19 +232,21 @@ var println = func(elems...) {
# sort function using quick sort
# not very efficient... :(
var sort = func(){
var sort = func() {
srand(); # be aware! this causes global changes
var quick_sort_core = func(vec, left, right, cmp) {
if(left>=right) return nil;
var base = left+int(rand()*(right-left));
(vec[left], vec[base]) = (vec[base], vec[left]);
var (i, j, tmp) = (left, right, vec[left]);
while(i<j){
while(i<j and cmp(tmp,vec[j]))
while(i<j) {
while(i<j and cmp(tmp,vec[j])) {
j -= 1;
}
vec[i] = vec[j];
while(i<j and cmp(vec[i],tmp))
while(i<j and cmp(vec[i],tmp)) {
i += 1;
}
vec[j] = vec[i];
j -= 1;
}
@ -249,7 +255,7 @@ var sort = func(){
quick_sort_core(vec, i+1, right, cmp);
return nil;
}
return func(vec, cmp = func(a, b) {return a<b;}){
return func(vec, cmp = func(a, b) {return a<b;}) {
quick_sort_core(vec, 0, size(vec)-1, cmp);
return nil;
}
@ -276,7 +282,7 @@ var isnum = func(x) {
}
var isscalar = func(s) {
var t=typeof(s);
var t = typeof(s);
return (t=="num" or t=="str")? 1:0;
}
@ -293,25 +299,29 @@ var ghosttype = func(ghost_object) {
}
# get the index of val in the vec
var vecindex = func(vec,val) {
forindex(var i;vec)
if(val==vec[i])
var vecindex = func(vec, val) {
forindex(var i; vec) {
if(val==vec[i]) {
return i;
}
}
return nil;
}
# check if the object is an instance of the class
var isa = func(object, class) {
if (!ishash(object)) {
return 0;
return false;
}
if(!contains(object, "parents") or !isvec(object.parents)) {
return 0;
return false;
}
foreach(var elem;object.parents)
if(elem==class or isa(elem, class))
return 1;
return 0;
foreach(var elem; object.parents) {
if(elem==class or isa(elem, class)) {
return true;
}
}
return false;
}
# assert aborts when condition is not true

View File

@ -1,22 +1,23 @@
# result.nas
# ValKmjolnir 2021
var Result=func(){
var (ok,err,flag)=(nil,"",1);
return{
Ok:func(val){
ok=val;
flag=0;
var new = func() {
var (ok, err, flag) = (nil, "", 1);
return {
Ok: func(val) {
ok = val;
flag = 0;
return me;
},
Err:func(info){
err=info;
flag=1;
Err: func(info) {
err = info;
flag = 1;
return me;
},
unwrap:func(){
if(flag)
unwrap: func() {
if (flag) {
die(err);
}
return ok;
}
};

View File

@ -191,7 +191,6 @@ var ansi_escape_sequence=func(){
}
unix.sleep(0.01);
}
}
# enable unicode

28
test/flush_screen.nas Normal file
View File

@ -0,0 +1,28 @@
import.module.libkey;
srand();
var chars = "abcdefghijklmnopqrstuvwxyz" ~
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" ~
"1234567890" ~
"!@#$%^&*()_+-=~`[]{}\\|'\";:,.<>/?";
chars = split("", chars);
print("\ec");
while(1) {
var key = libkey.nonblock();
if (key!=nil and chr(key)=="q") {
break;
}
var res = "\e[1;1H";
for(var i = 0; i<20; i+=1) {
for(var j = 0; j<40; j+=1) {
res ~= "\e[38;5;" ~ int(rand()*256) ~ ";1m";
res ~= chars[int(rand()*size(chars))];
res ~= "\e[0m ";
}
res ~= "\n";
}
print(res);
unix.sleep(1/15);
}

View File

@ -1,9 +1,7 @@
import.std.json;
import.std.process_bar;
var JSON = json.JSON;
var ss=JSON.stringify({
var ss = json.stringify({
vec:[0,1,2],
hash:{
m1:0,
@ -17,10 +15,11 @@ var ss=JSON.stringify({
empty_an:[[[[[[{}]]]]]],
function:func(){}
});
println(ss,"\n");
println(JSON.parse(ss),"\n");
var ss=JSON.stringify([{
println(ss, "\n");
println(json.parse(ss), "\n");
var ss = json.stringify([{
vec:[0,1,2,3],
hash:{
m1:0,
@ -33,12 +32,13 @@ var ss=JSON.stringify([{
empty_an:[[[[[{}]]]]],
function:func(){}
}]);
println(ss,"\n");
println(JSON.parse(ss),"\n");
println(ss, "\n");
println(json.parse(ss), "\n");
func {
var bar=process_bar.high_resolution_bar(30);
var tmp=[
var bar = process_bar.high_resolution_bar(30);
var tmp = [
{t0:nil},
{t1:nil},
{t2:nil},
@ -50,18 +50,18 @@ func {
];
srand();
foreach(var h;tmp) {
var name=keys(h)[0];
h[name]=[];
print("\e[1000D",bar.bar(0));
for(var i=0;i<500;i+=1) {
append(h[name],{id:i,content:int(rand()*1e7)});
print("\e[1000D",bar.bar((i+1)/500));
foreach(var h; tmp) {
var name = keys(h)[0];
h[name] = [];
print("\e[1000D", bar.bar(0));
for(var i = 0; i<500; i+=1) {
append(h[name], {id:i, content:int(rand()*1e7)});
print("\e[1000D", bar.bar((i+1)/500));
}
print("\e[1000D",bar.bar(1)," executing...\n");
print("\e[1000D", bar.bar(1), " executing...\n");
}
print("\e[1000D","\e["~str(size(tmp))~"A");
foreach(var h;JSON.parse(JSON.stringify(tmp))) {
println("\e[1000D",bar.bar(1)," parse done ",keys(h)[0]," ",size(h[keys(h)[0]]));
print("\e[1000D", "\e["~str(size(tmp))~"A");
foreach(var h; json.parse(json.stringify(tmp))) {
println("\e[1000D", bar.bar(1), " parse done ", keys(h)[0], " ", size(h[keys(h)[0]]));
}
}();

View File

@ -2,7 +2,6 @@ import.module.libsock;
import.std.json;
import.std.runtime;
var JSON = json.JSON;
var socket = libsock.socket;
var gettime=func(){
@ -79,19 +78,19 @@ var server=func(ip,port) {
while(1) {
var data=jsonRPC.recv(client);
if (data!=nil) {
data=JSON.parse(data);
data=json.parse(data);
} else {
break;
}
if (contains(methods,data.method)) {
jsonRPC.send(client, JSON.stringify({
jsonRPC.send(client, json.stringify({
jsonrpc:2.0,
id:data.id,
error:"null",
result:methods[data.method](data.params)
}));
} else {
jsonRPC.send(client, JSON.stringify({
jsonRPC.send(client, json.stringify({
jsonrpc:2.0,
id:data.id,
error:"no such method \\\""~data.method~"\\\"",
@ -113,13 +112,13 @@ var client=func(ip,port) {
var server=jsonRPC.connect(ip,port);
while(1) {
unix.sleep(5);
var data=JSON.stringify({jsonrpc:2.0, id:call_id, method:methods[rand()*size(methods)],params:params[rand()*size(params)]});
var data=json.stringify({jsonrpc:2.0, id:call_id, method:methods[rand()*size(methods)],params:params[rand()*size(params)]});
jsonRPC.send(server, data);
var respond=jsonRPC.recv(server);
if (respond==nil) {
break;
}
println("[",gettime(),"] result: ",JSON.parse(respond).result);
println("[",gettime(),"] result: ",json.parse(respond).result);
call_id+=1;
}
jsonRPC.disconnect(server);

View File

@ -234,4 +234,19 @@ for(var a=0;a<16;a+=1) {
}
print([0, 1, 2]~[3, 4, 5], "\n");
print(num("4.94065645841246544176568792868e-324"), "\n");
print(num("1.79769313486231570814527423731704357e+308"), "\n");
print(num("4.94065645841246544176568792868e-324"), "\n");
var test_call_iterator = {iter: 0};
foreach(test_call_iterator.iter; [0, 1, 2, 3]) {
println(test_call_iterator);
}
var test_single_id_iterator = 0;
foreach(test_single_id_iterator; [0, 1, 2, 3]) {
println(test_single_id_iterator);
}

28
tools/file2ppm.nas Normal file
View File

@ -0,0 +1,28 @@
var ppm = func(filename, buffer) {
# P3 use ASCII number
# P6 use binary character
var width = 256;
var height = int(size(buffer)/3/width); # ppm use 3 chars for one pixel
println("width ", width, ", height ", height);
var fd = io.open(filename, "wb");
io.write(fd, "P6\n"~width~" "~height~"\n255\n");
io.write(fd, buffer);
io.close(fd);
}
if (size(arg)<1) {
println("need input file and output file");
exit(-1);
}
var content = io.readfile(arg[0], "r");
var tail_len = 0;
while(math.mod(size(content), 256*3)!=0) {
content ~= "A";
tail_len += 1;
}
println("filled ", tail_len);
println("size ", size(content));
ppm(size(arg)==2? arg[1]:"out.ppm", content);