Merge pull request #33 from ValKmjolnir/develop
🐛 fix bug in debugger and symbol_finder & code improvement
This commit is contained in:
commit
d6e1408edb
|
@ -36,6 +36,7 @@ set(NASAL_OBJECT_SOURCE_FILE
|
||||||
${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp
|
${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/nasal_opcode.cpp
|
${CMAKE_SOURCE_DIR}/src/nasal_opcode.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/nasal_parse.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/nasal_vm.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/optimizer.cpp
|
${CMAKE_SOURCE_DIR}/src/optimizer.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/symbol_finder.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
|
set(MODULE_USED_OBJECT_SOURCE_FILE
|
||||||
${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp
|
${CMAKE_SOURCE_DIR}/src/nasal_misc.cpp
|
||||||
|
${CMAKE_SOURCE_DIR}/src/nasal_type.cpp
|
||||||
${CMAKE_SOURCE_DIR}/src/nasal_gc.cpp)
|
${CMAKE_SOURCE_DIR}/src/nasal_gc.cpp)
|
||||||
add_library(module-used-object STATIC ${MODULE_USED_OBJECT_SOURCE_FILE})
|
add_library(module-used-object STATIC ${MODULE_USED_OBJECT_SOURCE_FILE})
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
[](./LICENSE)
|
[](./LICENSE)
|
||||||
|
|
||||||
> This document is also available in: [__中文__](./doc/README_zh.md) | [__English__](./README.md)
|
> This document is also available in: [__中文__](./doc/README_zh.md) | [__English__](./README.md)
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
[](../LICENSE)
|
[](../LICENSE)
|
||||||
|
|
||||||
> 这篇文档包含多语言版本: [__中文__](../doc/README_zh.md) | [__English__](../README.md)
|
> 这篇文档包含多语言版本: [__中文__](../doc/README_zh.md) | [__English__](../README.md)
|
||||||
|
|
21
makefile
21
makefile
|
@ -1,11 +1,13 @@
|
||||||
STD = c++17
|
STD = c++17
|
||||||
OS = $(shell uname)
|
|
||||||
|
ifndef OS
|
||||||
|
OS = $(shell uname)
|
||||||
|
endif
|
||||||
ifeq ($(OS), Darwin)
|
ifeq ($(OS), Darwin)
|
||||||
CXXFLAGS = -std=$(STD) -c -O3 -fno-exceptions -fPIC -mmacosx-version-min=10.15
|
CXXFLAGS = -std=$(STD) -c -O3 -fno-exceptions -fPIC -mmacosx-version-min=10.15
|
||||||
else
|
else
|
||||||
CXXFLAGS = -std=$(STD) -c -O3 -fno-exceptions -fPIC
|
CXXFLAGS = -std=$(STD) -c -O3 -fno-exceptions -fPIC
|
||||||
endif
|
endif
|
||||||
CPPFLAGS = -I .
|
|
||||||
|
|
||||||
NASAL_HEADER=\
|
NASAL_HEADER=\
|
||||||
src/ast_dumper.h\
|
src/ast_dumper.h\
|
||||||
|
@ -21,6 +23,7 @@ NASAL_HEADER=\
|
||||||
src/nasal_opcode.h\
|
src/nasal_opcode.h\
|
||||||
src/nasal_parse.h\
|
src/nasal_parse.h\
|
||||||
src/nasal_vm.h\
|
src/nasal_vm.h\
|
||||||
|
src/nasal_type.h\
|
||||||
src/nasal.h\
|
src/nasal.h\
|
||||||
src/optimizer.h\
|
src/optimizer.h\
|
||||||
src/symbol_finder.h\
|
src/symbol_finder.h\
|
||||||
|
@ -55,6 +58,7 @@ NASAL_OBJECT=\
|
||||||
build/unix_lib.o\
|
build/unix_lib.o\
|
||||||
build/dylib_lib.o\
|
build/dylib_lib.o\
|
||||||
build/coroutine.o\
|
build/coroutine.o\
|
||||||
|
build/nasal_type.o\
|
||||||
build/nasal_vm.o\
|
build/nasal_vm.o\
|
||||||
build/nasal_dbg.o\
|
build/nasal_dbg.o\
|
||||||
build/repl.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
|
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
|
$(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
|
$(CXX) $(CXXFLAGS) src/nasal_gc.cpp -o build/nasal_gc.o
|
||||||
|
|
||||||
build/nasal_import.o: \
|
build/nasal_import.o: \
|
||||||
|
@ -113,18 +120,21 @@ build/nasal_ast.o: \
|
||||||
|
|
||||||
build/nasal_builtin.o: \
|
build/nasal_builtin.o: \
|
||||||
src/nasal.h\
|
src/nasal.h\
|
||||||
|
src/nasal_type.h\
|
||||||
src/nasal_gc.h\
|
src/nasal_gc.h\
|
||||||
src/nasal_builtin.h src/nasal_builtin.cpp | build
|
src/nasal_builtin.h src/nasal_builtin.cpp | build
|
||||||
$(CXX) $(CXXFLAGS) src/nasal_builtin.cpp -o build/nasal_builtin.o
|
$(CXX) $(CXXFLAGS) src/nasal_builtin.cpp -o build/nasal_builtin.o
|
||||||
|
|
||||||
build/coroutine.o: \
|
build/coroutine.o: \
|
||||||
src/nasal.h\
|
src/nasal.h\
|
||||||
|
src/nasal_type.h\
|
||||||
src/nasal_gc.h\
|
src/nasal_gc.h\
|
||||||
src/coroutine.h src/coroutine.cpp | build
|
src/coroutine.h src/coroutine.cpp | build
|
||||||
$(CXX) $(CXXFLAGS) src/coroutine.cpp -o build/coroutine.o
|
$(CXX) $(CXXFLAGS) src/coroutine.cpp -o build/coroutine.o
|
||||||
|
|
||||||
build/bits_lib.o: \
|
build/bits_lib.o: \
|
||||||
src/nasal.h\
|
src/nasal.h\
|
||||||
|
src/nasal_type.h\
|
||||||
src/nasal_gc.h\
|
src/nasal_gc.h\
|
||||||
src/bits_lib.h src/bits_lib.cpp | build
|
src/bits_lib.h src/bits_lib.cpp | build
|
||||||
$(CXX) $(CXXFLAGS) src/bits_lib.cpp -o build/bits_lib.o
|
$(CXX) $(CXXFLAGS) src/bits_lib.cpp -o build/bits_lib.o
|
||||||
|
@ -132,30 +142,35 @@ build/bits_lib.o: \
|
||||||
|
|
||||||
build/math_lib.o: \
|
build/math_lib.o: \
|
||||||
src/nasal.h\
|
src/nasal.h\
|
||||||
|
src/nasal_type.h\
|
||||||
src/nasal_gc.h\
|
src/nasal_gc.h\
|
||||||
src/math_lib.h src/math_lib.cpp | build
|
src/math_lib.h src/math_lib.cpp | build
|
||||||
$(CXX) $(CXXFLAGS) src/math_lib.cpp -o build/math_lib.o
|
$(CXX) $(CXXFLAGS) src/math_lib.cpp -o build/math_lib.o
|
||||||
|
|
||||||
build/io_lib.o: \
|
build/io_lib.o: \
|
||||||
src/nasal.h\
|
src/nasal.h\
|
||||||
|
src/nasal_type.h\
|
||||||
src/nasal_gc.h\
|
src/nasal_gc.h\
|
||||||
src/io_lib.h src/io_lib.cpp | build
|
src/io_lib.h src/io_lib.cpp | build
|
||||||
$(CXX) $(CXXFLAGS) src/io_lib.cpp -o build/io_lib.o
|
$(CXX) $(CXXFLAGS) src/io_lib.cpp -o build/io_lib.o
|
||||||
|
|
||||||
build/dylib_lib.o: \
|
build/dylib_lib.o: \
|
||||||
src/nasal.h\
|
src/nasal.h\
|
||||||
|
src/nasal_type.h\
|
||||||
src/nasal_gc.h\
|
src/nasal_gc.h\
|
||||||
src/dylib_lib.h src/dylib_lib.cpp | build
|
src/dylib_lib.h src/dylib_lib.cpp | build
|
||||||
$(CXX) $(CXXFLAGS) src/dylib_lib.cpp -o build/dylib_lib.o
|
$(CXX) $(CXXFLAGS) src/dylib_lib.cpp -o build/dylib_lib.o
|
||||||
|
|
||||||
build/unix_lib.o: \
|
build/unix_lib.o: \
|
||||||
src/nasal.h\
|
src/nasal.h\
|
||||||
|
src/nasal_type.h\
|
||||||
src/nasal_gc.h\
|
src/nasal_gc.h\
|
||||||
src/unix_lib.h src/unix_lib.cpp | build
|
src/unix_lib.h src/unix_lib.cpp | build
|
||||||
$(CXX) $(CXXFLAGS) src/unix_lib.cpp -o build/unix_lib.o
|
$(CXX) $(CXXFLAGS) src/unix_lib.cpp -o build/unix_lib.o
|
||||||
|
|
||||||
build/fg_props.o: \
|
build/fg_props.o: \
|
||||||
src/nasal.h\
|
src/nasal.h\
|
||||||
|
src/nasal_type.h\
|
||||||
src/nasal_gc.h\
|
src/nasal_gc.h\
|
||||||
src/fg_props.h src/fg_props.cpp | build
|
src/fg_props.h src/fg_props.cpp | build
|
||||||
$(CXX) $(CXXFLAGS) src/fg_props.cpp -o build/fg_props.o
|
$(CXX) $(CXXFLAGS) src/fg_props.cpp -o build/fg_props.o
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include "../src/nasal.h"
|
#include "../src/nasal.h"
|
||||||
|
#include "../src/nasal_type.h"
|
||||||
|
#include "../src/nasal_gc.h"
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
namespace fib_module {
|
namespace fib_module {
|
||||||
|
@ -18,14 +20,14 @@ var fib(var* args, usize size, gc* ngc) {
|
||||||
return nas_err("fib", "lack arguments");
|
return nas_err("fib", "lack arguments");
|
||||||
}
|
}
|
||||||
var num = args[0];
|
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) {
|
var quick_fib(var* args, usize size, gc* ngc) {
|
||||||
if (!size) {
|
if (!size) {
|
||||||
return nas_err("quick_fib","lack arguments");
|
return nas_err("quick_fib","lack arguments");
|
||||||
}
|
}
|
||||||
double num = args[0].tonum();
|
double num = args[0].to_num();
|
||||||
if (num<2) {
|
if (num<2) {
|
||||||
return var::num(num);
|
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 create_new_ghost(var* args, usize size, gc* ngc) {
|
||||||
var res = ngc->alloc(vm_obj);
|
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;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
var set_new_ghost(var* args, usize size, gc* ngc) {
|
var set_new_ghost(var* args, usize size, gc* ngc) {
|
||||||
var res = args[0];
|
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";
|
std::cout << "set_new_ghost: not ghost for test type.\n";
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
f64 num = args[1].num();
|
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";
|
std::cout << "set_new_ghost: successfully set ghost = " << num << "\n";
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
var print_new_ghost(var* args, usize size, gc* ngc) {
|
var print_new_ghost(var* args, usize size, gc* ngc) {
|
||||||
var res = args[0];
|
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";
|
std::cout << "print_new_ghost: not ghost for test type.\n";
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
std::cout << "print_new_ghost: " << res.obj() << " result = "
|
std::cout << "print_new_ghost: " << res.ghost() << " result = "
|
||||||
<< *((u32*)res.obj().ptr) << "\n";
|
<< *((u32*)res.ghost().pointer) << "\n";
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "../src/nasal.h"
|
#include "../src/nasal.h"
|
||||||
|
#include "../src/nasal_type.h"
|
||||||
|
#include "../src/nasal_gc.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
|
|
|
@ -3,11 +3,14 @@
|
||||||
dynamic_libs_so = libfib.so libkey.so libnasock.so libmat.so
|
dynamic_libs_so = libfib.so libkey.so libnasock.so libmat.so
|
||||||
dynamic_libs_dll = libfib.dll libkey.dll libnasock.dll libmat.dll
|
dynamic_libs_dll = libfib.dll libkey.dll libnasock.dll libmat.dll
|
||||||
|
|
||||||
used_header = ../src/nasal.h ../src/nasal_gc.h
|
used_header = ../src/nasal.h ../src/nasal_type.h ../src/nasal_gc.h
|
||||||
used_object = ../build/nasal_misc.o ../build/nasal_gc.o
|
used_object = ../build/nasal_misc.o ../build/nasal_type.o ../build/nasal_gc.o
|
||||||
|
|
||||||
STD = c++17
|
STD = c++17
|
||||||
OS = $(shell uname)
|
|
||||||
|
ifndef OS
|
||||||
|
OS = $(shell uname)
|
||||||
|
endif
|
||||||
ifeq ($(OS), Darwin)
|
ifeq ($(OS), Darwin)
|
||||||
CXXFLAGS = -std=$(STD) -c -O3 -fPIC -mmacosx-version-min=10.15
|
CXXFLAGS = -std=$(STD) -c -O3 -fPIC -mmacosx-version-min=10.15
|
||||||
else
|
else
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "../src/nasal.h"
|
#include "../src/nasal.h"
|
||||||
|
#include "../src/nasal_type.h"
|
||||||
|
#include "../src/nasal_gc.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "../src/nasal.h"
|
#include "../src/nasal.h"
|
||||||
|
#include "../src/nasal_type.h"
|
||||||
|
#include "../src/nasal_gc.h"
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
|
@ -386,7 +386,11 @@ bool ast_dumper::visit_for_expr(for_expr* node) {
|
||||||
|
|
||||||
bool ast_dumper::visit_iter_expr(iter_expr* node) {
|
bool ast_dumper::visit_iter_expr(iter_expr* node) {
|
||||||
dump_indent();
|
dump_indent();
|
||||||
std::cout << "iterator";
|
if (node->is_definition()) {
|
||||||
|
std::cout << "iterator_definition";
|
||||||
|
} else {
|
||||||
|
std::cout << "iterator";
|
||||||
|
}
|
||||||
std::cout << format_location(node->get_location());
|
std::cout << format_location(node->get_location());
|
||||||
push_indent();
|
push_indent();
|
||||||
set_last();
|
set_last();
|
||||||
|
|
|
@ -2,55 +2,62 @@
|
||||||
|
|
||||||
namespace nasal {
|
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>(
|
return var::num(static_cast<f64>(
|
||||||
static_cast<u32>(local[1].num()) ^
|
static_cast<u32>(local[1].num()) ^
|
||||||
static_cast<u32>(local[2].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>(
|
return var::num(static_cast<f64>(
|
||||||
static_cast<u32>(local[1].num()) &
|
static_cast<u32>(local[1].num()) &
|
||||||
static_cast<u32>(local[2].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>(
|
return var::num(static_cast<f64>(
|
||||||
static_cast<u32>(local[1].num()) |
|
static_cast<u32>(local[1].num()) |
|
||||||
static_cast<u32>(local[2].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>(~(
|
return var::num(static_cast<f64>(~(
|
||||||
static_cast<u32>(local[1].num()) &
|
static_cast<u32>(local[1].num()) &
|
||||||
static_cast<u32>(local[2].num())
|
static_cast<u32>(local[2].num())
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_u32not(var* local, gc& ngc) {
|
var builtin_u32not(context* ctx, gc* ngc) {
|
||||||
return var::num(static_cast<f64>(~static_cast<u32>(local[1].num())));
|
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);
|
// bits.fld(s,0,3);
|
||||||
// if s stores 10100010(162)
|
// if s stores 10100010(162)
|
||||||
// will get 101(5)
|
// will get 101(5)
|
||||||
var str = local[1];
|
auto local = ctx->localr;
|
||||||
var startbit = local[2];
|
auto str = local[1];
|
||||||
var length = local[3];
|
auto startbit = local[2];
|
||||||
if (str.type!=vm_str || str.val.gcobj->unmut) {
|
auto length = local[3];
|
||||||
return nas_err("fld", "\"str\" must be mutable string");
|
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) {
|
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 bit = static_cast<u32>(startbit.num());
|
||||||
u32 len = static_cast<u32>(length.num());
|
u32 len = static_cast<u32>(length.num());
|
||||||
if (bit+len>8*str.str().length()) {
|
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;
|
u32 res = 0;
|
||||||
auto& s = str.str();
|
auto& s = str.str();
|
||||||
|
@ -62,24 +69,25 @@ var builtin_fld(var* local, gc& ngc) {
|
||||||
return var::num(static_cast<f64>(res));
|
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);
|
// bits.sfld(s,0,3);
|
||||||
// if s stores 10100010(162)
|
// if s stores 10100010(162)
|
||||||
// will get 101(5) then this will be signed extended to
|
// will get 101(5) then this will be signed extended to
|
||||||
// 11111101(-3)
|
// 11111101(-3)
|
||||||
var str = local[1];
|
auto local = ctx->localr;
|
||||||
var startbit = local[2];
|
auto str = local[1];
|
||||||
var length = local[3];
|
auto startbit = local[2];
|
||||||
if (str.type!=vm_str || str.val.gcobj->unmut) {
|
auto length = local[3];
|
||||||
return nas_err("sfld", "\"str\" must be mutable string");
|
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) {
|
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 bit = static_cast<u32>(startbit.num());
|
||||||
u32 len = static_cast<u32>(length.num());
|
u32 len = static_cast<u32>(length.num());
|
||||||
if (bit+len>8*str.str().length()) {
|
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;
|
u32 res = 0;
|
||||||
auto& s = str.str();
|
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)));
|
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);
|
// bits.setfld(s,0,8,69);
|
||||||
// set 01000101(69) to string will get this:
|
// set 01000101(69) to string will get this:
|
||||||
// 10100010(162)
|
// 10100010(162)
|
||||||
// so s[0]=162
|
// so s[0]=162
|
||||||
var str = local[1];
|
auto local = ctx->localr;
|
||||||
var startbit = local[2];
|
auto str = local[1];
|
||||||
var length = local[3];
|
auto startbit = local[2];
|
||||||
var value = local[4];
|
auto length = local[3];
|
||||||
if (str.type!=vm_str || str.val.gcobj->unmut) {
|
auto value = local[4];
|
||||||
return nas_err("setfld", "\"str\" must be mutable string");
|
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) {
|
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 bit = static_cast<u32>(startbit.num());
|
||||||
u32 len = static_cast<u32>(length.num());
|
u32 len = static_cast<u32>(length.num());
|
||||||
u64 val = static_cast<u64>(value.num());
|
u64 val = static_cast<u64>(value.num());
|
||||||
if (bit+len>8*str.str().length()) {
|
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();
|
auto& s = str.str();
|
||||||
for(u32 i = bit; i<bit+len; ++i) {
|
for(u32 i = bit; i<bit+len; ++i) {
|
||||||
|
@ -126,12 +137,12 @@ var builtin_setfld(var* local, gc& ngc) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_buf(var* local, gc& ngc) {
|
var builtin_buf(context* ctx, gc* ngc) {
|
||||||
var length = local[1];
|
var length = ctx->localr[1];
|
||||||
if (length.type!=vm_num || length.num()<=0) {
|
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();
|
auto& s = str.str();
|
||||||
s.resize(length.num(), '\0');
|
s.resize(length.num(), '\0');
|
||||||
return str;
|
return str;
|
||||||
|
|
|
@ -6,15 +6,15 @@
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
||||||
var builtin_u32xor(var*, gc&);
|
var builtin_u32xor(context*, gc*);
|
||||||
var builtin_u32and(var*, gc&);
|
var builtin_u32and(context*, gc*);
|
||||||
var builtin_u32or(var*, gc&);
|
var builtin_u32or(context*, gc*);
|
||||||
var builtin_u32nand(var*, gc&);
|
var builtin_u32nand(context*, gc*);
|
||||||
var builtin_u32not(var*, gc&);
|
var builtin_u32not(context*, gc*);
|
||||||
var builtin_fld(var*, gc&);
|
var builtin_fld(context*, gc*);
|
||||||
var builtin_sfld(var*, gc&);
|
var builtin_sfld(context*, gc*);
|
||||||
var builtin_setfld(var*, gc&);
|
var builtin_setfld(context*, gc*);
|
||||||
var builtin_buf(var*, gc&);
|
var builtin_buf(context*, gc*);
|
||||||
|
|
||||||
extern nasal_builtin_table bits_native[];
|
extern nasal_builtin_table bits_native[];
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
||||||
var builtin_cocreate(var* local, gc& ngc) {
|
var builtin_cocreate(context* ctx, gc* ngc) {
|
||||||
|
// ```
|
||||||
// +-------------+
|
// +-------------+
|
||||||
// | old pc | <- top[0]
|
// | old pc | <- top[0]
|
||||||
// +-------------+
|
// +-------------+
|
||||||
|
@ -15,55 +16,73 @@ var builtin_cocreate(var* local, gc& ngc) {
|
||||||
// +-------------+ <- local pointer stored in localr
|
// +-------------+ <- local pointer stored in localr
|
||||||
// | old funcr | <- old function stored in funcr
|
// | old funcr | <- old function stored in funcr
|
||||||
// +-------------+
|
// +-------------+
|
||||||
var func = local[1];
|
// ```
|
||||||
if (func.type!=vm_func) {
|
auto coroutine_function = ctx->localr[1];
|
||||||
return nas_err("coroutine::create", "must use a function to create coroutine");
|
if (coroutine_function.type!=vm_func) {
|
||||||
|
return nas_err(
|
||||||
|
"coroutine::create",
|
||||||
|
"must use a function to create coroutine"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (ngc.cort) {
|
if (ngc->cort) {
|
||||||
return nas_err("coroutine::create", "cannot create another coroutine in a coroutine");
|
return nas_err(
|
||||||
|
"coroutine::create",
|
||||||
|
"cannot create another coroutine in a coroutine"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
var co = ngc.alloc(vm_co);
|
auto coroutine_object = ngc->alloc(vm_co);
|
||||||
nas_co& cort = co.co();
|
auto& coroutine = coroutine_object.co();
|
||||||
cort.ctx.pc = func.func().entry-1;
|
coroutine.ctx.pc = coroutine_function.func().entry-1;
|
||||||
|
|
||||||
cort.ctx.top[0] = nil;
|
coroutine.ctx.top[0] = nil;
|
||||||
cort.ctx.localr = cort.ctx.top+1;
|
coroutine.ctx.localr = coroutine.ctx.top+1;
|
||||||
cort.ctx.top = cort.ctx.localr+func.func().lsize;
|
coroutine.ctx.top = coroutine.ctx.localr +
|
||||||
cort.ctx.localr[0] = func.func().local[0];
|
coroutine_function.func().local_size;
|
||||||
cort.ctx.top[0] = nil; // old upvalr
|
coroutine.ctx.localr[0] = coroutine_function.func().local[0];
|
||||||
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
|
|
||||||
|
|
||||||
cort.ctx.funcr = func; // make sure the coroutine function can use correct upvalues
|
// store old upvalr on stack
|
||||||
cort.status = nas_co::status::suspended;
|
coroutine.ctx.top[0] = nil;
|
||||||
|
coroutine.ctx.top++;
|
||||||
return co;
|
|
||||||
|
// 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) {
|
var builtin_coresume(context* ctx, gc* ngc) {
|
||||||
if (ngc.cort) {
|
if (ngc->cort) {
|
||||||
return nas_err("coroutine::resume", "cannot start another coroutine when one is running");
|
return nas_err(
|
||||||
|
"coroutine::resume",
|
||||||
|
"cannot start another coroutine when one is running"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
var co = local[1];
|
auto main_local_frame = ctx->localr;
|
||||||
// return nil if is not a coroutine object
|
auto coroutine_object = main_local_frame[1];
|
||||||
if (co.type!=vm_co) {
|
// return nil if is not a coroutine object or coroutine exited
|
||||||
return nil;
|
if (coroutine_object.type!=vm_co ||
|
||||||
}
|
coroutine_object.co().status==nas_co::status::dead) {
|
||||||
// cannot resume a dead coroutine
|
|
||||||
if (co.co().status==nas_co::status::dead) {
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
// change to coroutine context
|
// change to coroutine context
|
||||||
ngc.ctxchg(co.co());
|
ngc->context_change(&coroutine_object.co());
|
||||||
|
|
||||||
// fetch coroutine's stack top and return
|
// 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
|
// 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
|
// 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
|
// 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
|
// the coroutine seems like coroutine.yield returns the value
|
||||||
// but in fact coroutine.yield stop the coroutine
|
// but in fact coroutine.yield stop the coroutine
|
||||||
// until main context calls the coroutine.resume
|
// until main context calls the coroutine.resume
|
||||||
return local[2];
|
return main_local_frame[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_coyield(var* local, gc& ngc) {
|
var builtin_coyield(context* ctx, gc* ngc) {
|
||||||
if (!ngc.cort) {
|
if (!ngc->cort) {
|
||||||
return nas_err("coroutine::yield", "no coroutine is running");
|
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]
|
// then this will return value to main's stack top[0]
|
||||||
// the procedure seems like coroutine.resume returns the value
|
// the procedure seems like coroutine.resume returns the value
|
||||||
// but in fact coroutine.resume stop the main context
|
// but in fact coroutine.resume stop the main context
|
||||||
// until coroutine calls the coroutine.yield
|
// until coroutine calls the coroutine.yield
|
||||||
return local[1];
|
return coroutine_local_frame[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_costatus(var* local, gc& ngc) {
|
var builtin_costatus(context* ctx, gc* ngc) {
|
||||||
var co = local[1];
|
auto coroutine_object = ctx->localr[1];
|
||||||
if (co.type!=vm_co) {
|
if (coroutine_object.type!=vm_co) {
|
||||||
return ngc.newstr("error");
|
return ngc->newstr("error");
|
||||||
}
|
}
|
||||||
switch(co.co().status) {
|
switch(coroutine_object.co().status) {
|
||||||
case nas_co::status::suspended: return ngc.newstr("suspended");
|
case nas_co::status::suspended: return ngc->newstr("suspended");
|
||||||
case nas_co::status::running: return ngc.newstr("running");
|
case nas_co::status::running: return ngc->newstr("running");
|
||||||
case nas_co::status::dead: return ngc.newstr("dead");
|
case nas_co::status::dead: return ngc->newstr("dead");
|
||||||
}
|
}
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_corun(var* local, gc& ngc) {
|
var builtin_corun(context* ctx, gc* ngc) {
|
||||||
return ngc.cort? one:zero;
|
return ngc->cort? one:zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
nasal_builtin_table coroutine_native[] = {
|
nasal_builtin_table coroutine_native[] = {
|
||||||
|
|
|
@ -6,11 +6,11 @@
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
||||||
var builtin_cocreate(var*, gc&);
|
var builtin_cocreate(context*, gc*);
|
||||||
var builtin_coresume(var*, gc&);
|
var builtin_coresume(context*, gc*);
|
||||||
var builtin_coyield(var*, gc&);
|
var builtin_coyield(context*, gc*);
|
||||||
var builtin_costatus(var*, gc&);
|
var builtin_costatus(context*, gc*);
|
||||||
var builtin_corun(var*, gc&);
|
var builtin_corun(context*, gc*);
|
||||||
|
|
||||||
extern nasal_builtin_table coroutine_native[];
|
extern nasal_builtin_table coroutine_native[];
|
||||||
|
|
||||||
|
|
|
@ -2,107 +2,130 @@
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
||||||
const auto dylib_type_name = "dylib";
|
const auto dynamic_library_type_name = "dylib";
|
||||||
const auto func_addr_type_name = "faddr";
|
const auto function_address_type_name = "faddr";
|
||||||
|
|
||||||
void dylib_destructor(void* ptr) {
|
void dynamic_library_destructor(void* pointer) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
FreeLibrary(static_cast<HMODULE>(ptr));
|
FreeLibrary(static_cast<HMODULE>(pointer));
|
||||||
#else
|
#else
|
||||||
dlclose(ptr);
|
dlclose(pointer);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void func_addr_destructor(void* ptr) {}
|
var builtin_dlopen(context* ctx, gc* ngc) {
|
||||||
|
auto dlname = ctx->localr[1];
|
||||||
var builtin_dlopen(var* local, gc& ngc) {
|
|
||||||
var dlname = local[1];
|
|
||||||
if (dlname.type!=vm_str) {
|
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
|
#ifdef _WIN32
|
||||||
void* func = (void*)GetProcAddress(
|
wchar_t* wide_string = new wchar_t[dlname.str().size()+1];
|
||||||
static_cast<HMODULE>(lib.obj().ptr),
|
if (!wide_string) {
|
||||||
"get"
|
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
|
#else
|
||||||
void* func = dlsym(lib.obj().ptr, "get");
|
void* register_table_get_function = dlsym(
|
||||||
|
library_object.ghost().pointer, "get"
|
||||||
|
);
|
||||||
#endif
|
#endif
|
||||||
if (!func) {
|
if (!register_table_get_function) {
|
||||||
return nas_err("dlopen", "cannot find <get> function");
|
return nas_err("dylib::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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngc.temp = nil;
|
// get function pointer by name
|
||||||
return ret;
|
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 builtin_dlclose(context* ctx, gc* ngc) {
|
||||||
var libptr = local[1];
|
auto library_pointer = ctx->localr[1];
|
||||||
if (!libptr.objchk(dylib_type_name)) {
|
if (!library_pointer.object_check(dynamic_library_type_name)) {
|
||||||
return nas_err("dlclose", "\"lib\" is not a valid dynamic lib");
|
return nas_err("dylib::dlclose", "\"lib\" is not a valid dynamic lib");
|
||||||
}
|
}
|
||||||
libptr.obj().clear();
|
library_pointer.ghost().clear();
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_dlcallv(var* local, gc& ngc) {
|
var builtin_dlcallv(context* ctx, gc* ngc) {
|
||||||
var fp = local[1];
|
auto function_object = ctx->localr[1];
|
||||||
var args = local[2];
|
auto arguments = ctx->localr[2];
|
||||||
if (!fp.objchk(func_addr_type_name)) {
|
if (!function_object.object_check(function_address_type_name)) {
|
||||||
return nas_err("dlcall", "\"ptr\" is not a valid function pointer");
|
return nas_err("dylib::dlcall",
|
||||||
|
"\"ptr\" is not a valid function pointer"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
auto& vec = args.vec().elems;
|
auto& vec = arguments.vec().elems;
|
||||||
return reinterpret_cast<module_func>(fp.obj().ptr)(
|
return reinterpret_cast<module_func>(function_object.ghost().pointer)(
|
||||||
vec.data(),
|
vec.data(),
|
||||||
vec.size(),
|
vec.size(),
|
||||||
&ngc
|
ngc
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_dlcall(var* local, gc& ngc) {
|
var builtin_dlcall(context* ctx, gc* ngc) {
|
||||||
var fp = local[1];
|
auto function_object = ctx->localr[1];
|
||||||
if (!fp.objchk(func_addr_type_name)) {
|
if (!function_object.object_check(function_address_type_name)) {
|
||||||
return nas_err("dlcall", "\"ptr\" is not a valid function pointer");
|
return nas_err("dylib::dlcall",
|
||||||
|
"\"ptr\" is not a valid function pointer"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
var* local_frame_start = local+2;
|
// function pointer is at ctx->localr[1]
|
||||||
usize local_frame_size = ngc.rctx->top-local_frame_start;
|
// so arguments starts from ctx->localr[2]
|
||||||
// arguments' stored place begins at local +2
|
var* local_frame_start = ctx->localr + 2;
|
||||||
return reinterpret_cast<module_func>(fp.obj().ptr)(
|
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_start,
|
||||||
local_frame_size,
|
local_frame_size,
|
||||||
&ngc
|
ngc
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,13 +13,12 @@
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
||||||
void dylib_destructor(void*);
|
void dynamic_library_destructor(void*);
|
||||||
void func_addr_destructor(void*);
|
|
||||||
|
|
||||||
var builtin_dlopen(var*, gc&);
|
var builtin_dlopen(context*, gc*);
|
||||||
var builtin_dlclose(var*, gc&);
|
var builtin_dlclose(context*, gc*);
|
||||||
var builtin_dlcallv(var*, gc&);
|
var builtin_dlcallv(context*, gc*);
|
||||||
var builtin_dlcall(var*, gc&);
|
var builtin_dlcall(context*, gc*);
|
||||||
|
|
||||||
extern nasal_builtin_table dylib_lib_native[];
|
extern nasal_builtin_table dylib_lib_native[];
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,12 @@
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
||||||
var builtin_logprint(var* local, gc& ngc) {
|
var builtin_logprint(context* ctx, gc* ngc) {
|
||||||
var level = local[1];
|
auto local = ctx->localr;
|
||||||
var elems = local[2];
|
auto level = local[1];
|
||||||
|
auto elems = local[2];
|
||||||
if (elems.type!=vm_vec) {
|
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);
|
std::ofstream out("fgfs.log", std::ios::app);
|
||||||
switch (static_cast<u32>(level.num())) {
|
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_DEV_ALERT: out << "[DEV_ALERT]"; break;
|
||||||
case SG_MANDATORY_INFO: out << "[MANDATORY_INFO]"; break;
|
case SG_MANDATORY_INFO: out << "[MANDATORY_INFO]"; break;
|
||||||
default:
|
default:
|
||||||
return nas_err("logprint",
|
return nas_err("fg_env::logprint",
|
||||||
"incorrect log level " +
|
"incorrect log level " + std::to_string(level.num())
|
||||||
std::to_string(level.num())
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
for(auto& i : elems.vec().elems) {
|
for(auto& value : elems.vec().elems) {
|
||||||
out << i << " ";
|
out << value << " ";
|
||||||
}
|
}
|
||||||
out << "\n";
|
out << "\n";
|
||||||
return nil;
|
return nil;
|
||||||
|
|
|
@ -15,7 +15,7 @@ namespace nasal {
|
||||||
#define SG_DEV_ALERT 8
|
#define SG_DEV_ALERT 8
|
||||||
#define SG_MANDATORY_INFO 9
|
#define SG_MANDATORY_INFO 9
|
||||||
|
|
||||||
var builtin_logprint(var*, gc&);
|
var builtin_logprint(context*, gc*);
|
||||||
|
|
||||||
extern nasal_builtin_table flight_gear_native[];
|
extern nasal_builtin_table flight_gear_native[];
|
||||||
|
|
||||||
|
|
233
src/io_lib.cpp
233
src/io_lib.cpp
|
@ -11,187 +11,200 @@ void filehandle_destructor(void* ptr) {
|
||||||
fclose(static_cast<FILE*>(ptr));
|
fclose(static_cast<FILE*>(ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_readfile(var* local, gc& ngc) {
|
var builtin_readfile(context* ctx, gc* ngc) {
|
||||||
var val = local[1];
|
auto filename = ctx->localr[1];
|
||||||
if (val.type!=vm_str) {
|
if (filename.type!=vm_str) {
|
||||||
return nas_err("io::readfile", "\"filename\" must be string");
|
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;
|
std::stringstream rd;
|
||||||
if (!in.fail()) {
|
if (!in.fail()) {
|
||||||
rd << in.rdbuf();
|
rd << in.rdbuf();
|
||||||
}
|
}
|
||||||
return ngc.newstr(rd.str());
|
return ngc->newstr(rd.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_fout(var* local, gc& ngc) {
|
var builtin_fout(context* ctx, gc* ngc) {
|
||||||
var val = local[1];
|
auto local = ctx->localr;
|
||||||
var str = local[2];
|
auto filename = local[1];
|
||||||
if (val.type!=vm_str) {
|
auto source = local[2];
|
||||||
|
if (filename.type!=vm_str) {
|
||||||
return nas_err("io::fout", "\"filename\" must be string");
|
return nas_err("io::fout", "\"filename\" must be string");
|
||||||
}
|
}
|
||||||
std::ofstream out(val.str());
|
std::ofstream out(filename.str());
|
||||||
if (out.fail()) {
|
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;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_exists(var* local, gc& ngc) {
|
var builtin_exists(context* ctx, gc* ngc) {
|
||||||
if (local[1].type!=vm_str) {
|
auto filename = ctx->localr[1];
|
||||||
|
if (filename.type!=vm_str) {
|
||||||
return zero;
|
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 builtin_open(context* ctx, gc* ngc) {
|
||||||
var name = local[1];
|
auto local = ctx->localr;
|
||||||
var mode = local[2];
|
auto name = local[1];
|
||||||
|
auto mode = local[2];
|
||||||
if (name.type!=vm_str) {
|
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) {
|
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());
|
auto file_descriptor = fopen(name.str().c_str(), mode.str().c_str());
|
||||||
if (!res) {
|
if (!file_descriptor) {
|
||||||
return nas_err("open", "failed to open file <"+name.str()+">");
|
return nas_err("io::open", "failed to open file <" + name.str() + ">");
|
||||||
}
|
}
|
||||||
var ret = ngc.alloc(vm_obj);
|
var return_object = ngc->alloc(vm_obj);
|
||||||
ret.obj().set(file_type_name, filehandle_destructor, res);
|
return_object.ghost().set(
|
||||||
return ret;
|
file_type_name, filehandle_destructor, file_descriptor
|
||||||
|
);
|
||||||
|
return return_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_close(var* local, gc& ngc) {
|
var builtin_close(context* ctx, gc* ngc) {
|
||||||
var fd = local[1];
|
var file_descriptor = ctx->localr[1];
|
||||||
if (!fd.objchk(file_type_name)) {
|
if (!file_descriptor.object_check(file_type_name)) {
|
||||||
return nas_err("close", "not a valid filehandle");
|
return nas_err("io::close", "not a valid filehandle");
|
||||||
}
|
}
|
||||||
fd.obj().clear();
|
file_descriptor.ghost().clear();
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_read(var* local, gc& ngc) {
|
var builtin_read(context* ctx, gc* ngc) {
|
||||||
var fd = local[1];
|
auto local = ctx->localr;
|
||||||
var buf = local[2];
|
auto file_descriptor = local[1];
|
||||||
var len = local[3];
|
auto buffer = local[2];
|
||||||
if (!fd.objchk(file_type_name)) {
|
auto length = local[3];
|
||||||
return nas_err("read", "not a valid filehandle");
|
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) {
|
if (buffer.type!=vm_str || buffer.val.gcobj->unmutable) {
|
||||||
return nas_err("read", "\"buf\" must be mutable string");
|
return nas_err("io::read", "\"buf\" must be mutable string");
|
||||||
}
|
}
|
||||||
if (len.type!=vm_num) {
|
if (length.type!=vm_num) {
|
||||||
return nas_err("read", "\"len\" must be number");
|
return nas_err("io::read", "\"len\" must be number");
|
||||||
}
|
}
|
||||||
if (len.num()<=0 || len.num()>=(1<<30)) {
|
if (length.num()<=0 || length.num()>=(1<<30)) {
|
||||||
return nas_err("read", "\"len\" less than 1 or too large");
|
return nas_err("io::read", "\"len\" less than 1 or too large");
|
||||||
}
|
}
|
||||||
char* buff = new char[(usize)len.num()+1];
|
auto temp_buffer = new char[static_cast<usize>(length.num())+1];
|
||||||
if (!buff) {
|
if (!temp_buffer) {
|
||||||
return nas_err("read", "malloc failed");
|
return nas_err("io::read", "malloc failed");
|
||||||
}
|
}
|
||||||
f64 res = fread(buff, 1, len.num(), static_cast<FILE*>(fd.obj().ptr));
|
auto read_size = fread(
|
||||||
buf.str() = buff;
|
temp_buffer, 1, length.num(),
|
||||||
buf.val.gcobj->unmut = true;
|
static_cast<FILE*>(file_descriptor.ghost().pointer)
|
||||||
delete []buff;
|
);
|
||||||
return var::num(res);
|
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 builtin_write(context* ctx, gc* ngc) {
|
||||||
var fd = local[1];
|
auto local = ctx->localr;
|
||||||
var str = local[2];
|
auto file_descriptor = local[1];
|
||||||
if (!fd.objchk(file_type_name)) {
|
auto source = local[2];
|
||||||
return nas_err("write", "not a valid filehandle");
|
if (!file_descriptor.object_check(file_type_name)) {
|
||||||
|
return nas_err("io::write", "not a valid filehandle");
|
||||||
}
|
}
|
||||||
if (str.type!=vm_str) {
|
if (source.type!=vm_str) {
|
||||||
return nas_err("write", "\"str\" must be string");
|
return nas_err("io::write", "\"str\" must be string");
|
||||||
}
|
}
|
||||||
return var::num(static_cast<f64>(fwrite(
|
return var::num(static_cast<f64>(fwrite(
|
||||||
str.str().c_str(),
|
source.str().c_str(), 1, source.str().length(),
|
||||||
1,
|
static_cast<FILE*>(file_descriptor.ghost().pointer)
|
||||||
str.str().length(),
|
|
||||||
static_cast<FILE*>(fd.obj().ptr)
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_seek(var* local, gc& ngc) {
|
var builtin_seek(context* ctx, gc* ngc) {
|
||||||
var fd = local[1];
|
auto local = ctx->localr;
|
||||||
var pos = local[2];
|
auto file_descriptor = local[1];
|
||||||
var whence = local[3];
|
auto position = local[2];
|
||||||
if (!fd.objchk(file_type_name)) {
|
auto whence = local[3];
|
||||||
return nas_err("seek", "not a valid filehandle");
|
if (!file_descriptor.object_check(file_type_name)) {
|
||||||
|
return nas_err("io::seek", "not a valid filehandle");
|
||||||
}
|
}
|
||||||
return var::num(static_cast<f64>(fseek(
|
return var::num(static_cast<f64>(fseek(
|
||||||
static_cast<FILE*>(fd.obj().ptr),
|
static_cast<FILE*>(file_descriptor.ghost().pointer),
|
||||||
pos.num(),
|
position.num(),
|
||||||
whence.num()
|
whence.num()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_tell(var* local, gc& ngc) {
|
var builtin_tell(context* ctx, gc* ngc) {
|
||||||
var fd = local[1];
|
auto file_descriptor = ctx->localr[1];
|
||||||
if (!fd.objchk(file_type_name)) {
|
if (!file_descriptor.object_check(file_type_name)) {
|
||||||
return nas_err("tell", "not a valid filehandle");
|
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 builtin_readln(context* ctx, gc* ngc) {
|
||||||
var fd = local[1];
|
auto file_descriptor = ctx->localr[1];
|
||||||
if (!fd.objchk(file_type_name)) {
|
if (!file_descriptor.object_check(file_type_name)) {
|
||||||
return nas_err("readln", "not a valid filehandle");
|
return nas_err("io::readln", "not a valid filehandle");
|
||||||
}
|
}
|
||||||
var str = ngc.alloc(vm_str);
|
auto result = ngc->alloc(vm_str);
|
||||||
char c;
|
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') {
|
if (c=='\r') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (c=='\n') {
|
if (c=='\n') {
|
||||||
return str;
|
return result;
|
||||||
}
|
}
|
||||||
str.str() += c;
|
result.str().push_back(c);
|
||||||
}
|
}
|
||||||
if (str.str().length()) {
|
if (result.str().length()) {
|
||||||
return str;
|
return result;
|
||||||
}
|
}
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_stat(var* local, gc& ngc) {
|
var builtin_stat(context* ctx, gc* ngc) {
|
||||||
var name = local[1];
|
auto name = ctx->localr[1];
|
||||||
if (name.type!=vm_str) {
|
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;
|
struct stat buffer;
|
||||||
if (stat(name.str().c_str(),&buf)<0) {
|
if (stat(name.str().c_str(), &buffer)<0) {
|
||||||
return nas_err("stat", "failed to open file <"+name.str()+">");
|
return nas_err("io::stat", "failed to open file <" + name.str() + ">");
|
||||||
}
|
}
|
||||||
var ret = ngc.alloc(vm_vec);
|
auto result = ngc->alloc(vm_vec);
|
||||||
ret.vec().elems = {
|
result.vec().elems = {
|
||||||
var::num(static_cast<f64>(buf.st_dev)),
|
var::num(static_cast<f64>(buffer.st_dev)),
|
||||||
var::num(static_cast<f64>(buf.st_ino)),
|
var::num(static_cast<f64>(buffer.st_ino)),
|
||||||
var::num(static_cast<f64>(buf.st_mode)),
|
var::num(static_cast<f64>(buffer.st_mode)),
|
||||||
var::num(static_cast<f64>(buf.st_nlink)),
|
var::num(static_cast<f64>(buffer.st_nlink)),
|
||||||
var::num(static_cast<f64>(buf.st_uid)),
|
var::num(static_cast<f64>(buffer.st_uid)),
|
||||||
var::num(static_cast<f64>(buf.st_gid)),
|
var::num(static_cast<f64>(buffer.st_gid)),
|
||||||
var::num(static_cast<f64>(buf.st_rdev)),
|
var::num(static_cast<f64>(buffer.st_rdev)),
|
||||||
var::num(static_cast<f64>(buf.st_size)),
|
var::num(static_cast<f64>(buffer.st_size)),
|
||||||
var::num(static_cast<f64>(buf.st_atime)),
|
var::num(static_cast<f64>(buffer.st_atime)),
|
||||||
var::num(static_cast<f64>(buf.st_mtime)),
|
var::num(static_cast<f64>(buffer.st_mtime)),
|
||||||
var::num(static_cast<f64>(buf.st_ctime))
|
var::num(static_cast<f64>(buffer.st_ctime))
|
||||||
};
|
};
|
||||||
return ret;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_eof(var* local, gc& ngc) {
|
var builtin_eof(context* ctx, gc* ngc) {
|
||||||
var fd = local[1];
|
auto file_descriptor = ctx->localr[1];
|
||||||
if (!fd.objchk(file_type_name)) {
|
if (!file_descriptor.object_check(file_type_name)) {
|
||||||
return nas_err("readln", "not a valid filehandle");
|
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[] = {
|
nasal_builtin_table io_lib_native[] = {
|
||||||
|
|
30
src/io_lib.h
30
src/io_lib.h
|
@ -6,6 +6,12 @@
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
#include <unistd.h>
|
||||||
|
#else
|
||||||
|
#include <io.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#define F_OK 0 // fuck msc
|
#define F_OK 0 // fuck msc
|
||||||
#endif
|
#endif
|
||||||
|
@ -14,18 +20,18 @@ namespace nasal {
|
||||||
|
|
||||||
void filehandle_destructor(void*);
|
void filehandle_destructor(void*);
|
||||||
|
|
||||||
var builtin_readfile(var*, gc&);
|
var builtin_readfile(context*, gc*);
|
||||||
var builtin_fout(var*, gc&);
|
var builtin_fout(context*, gc*);
|
||||||
var builtin_exists(var*, gc&);
|
var builtin_exists(context*, gc*);
|
||||||
var builtin_open(var*, gc&);
|
var builtin_open(context*, gc*);
|
||||||
var builtin_close(var*, gc&);
|
var builtin_close(context*, gc*);
|
||||||
var builtin_read(var*, gc&);
|
var builtin_read(context*, gc*);
|
||||||
var builtin_write(var*, gc&);
|
var builtin_write(context*, gc*);
|
||||||
var builtin_seek(var*, gc&);
|
var builtin_seek(context*, gc*);
|
||||||
var builtin_tell(var*, gc&);
|
var builtin_tell(context*, gc*);
|
||||||
var builtin_readln(var*, gc&);
|
var builtin_readln(context*, gc*);
|
||||||
var builtin_stat(var*, gc&);
|
var builtin_stat(context*, gc*);
|
||||||
var builtin_eof(var*, gc&);
|
var builtin_eof(context*, gc*);
|
||||||
|
|
||||||
extern nasal_builtin_table io_lib_native[];
|
extern nasal_builtin_table io_lib_native[];
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
#include "nasal.h"
|
#include "nasal.h"
|
||||||
|
#include "nasal_type.h"
|
||||||
|
#include "nasal_gc.h"
|
||||||
#include "nasal_err.h"
|
#include "nasal_err.h"
|
||||||
#include "nasal_lexer.h"
|
#include "nasal_lexer.h"
|
||||||
#include "nasal_ast.h"
|
#include "nasal_ast.h"
|
||||||
|
|
|
@ -2,62 +2,62 @@
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
||||||
var builtin_pow(var* local, gc& ngc) {
|
var builtin_pow(context* ctx, gc* ngc) {
|
||||||
var x = local[1];
|
auto x = ctx->localr[1];
|
||||||
var y = local[2];
|
auto y = ctx->localr[2];
|
||||||
if (x.type!=vm_num || y.type!=vm_num) {
|
if (x.type!=vm_num || y.type!=vm_num) {
|
||||||
return var::num(std::nan(""));
|
return var::num(std::nan(""));
|
||||||
}
|
}
|
||||||
return var::num(std::pow(x.num(), y.num()));
|
return var::num(std::pow(x.num(), y.num()));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_sin(var* local, gc& ngc) {
|
var builtin_sin(context* ctx, gc* ngc) {
|
||||||
var val = local[1];
|
auto val = ctx->localr[1];
|
||||||
return var::num(val.type==vm_num? sin(val.num()):std::nan(""));
|
return var::num(val.type==vm_num? sin(val.num()):std::nan(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_cos(var* local, gc& ngc) {
|
var builtin_cos(context* ctx, gc* ngc) {
|
||||||
var val = local[1];
|
auto val = ctx->localr[1];
|
||||||
return var::num(val.type==vm_num? cos(val.num()):std::nan(""));
|
return var::num(val.type==vm_num? cos(val.num()):std::nan(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_tan(var* local, gc& ngc) {
|
var builtin_tan(context* ctx, gc* ngc) {
|
||||||
var val = local[1];
|
auto val = ctx->localr[1];
|
||||||
return var::num(val.type==vm_num? tan(val.num()):std::nan(""));
|
return var::num(val.type==vm_num? tan(val.num()):std::nan(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_exp(var* local, gc& ngc) {
|
var builtin_exp(context* ctx, gc* ngc) {
|
||||||
var val = local[1];
|
auto val = ctx->localr[1];
|
||||||
return var::num(val.type==vm_num? exp(val.num()):std::nan(""));
|
return var::num(val.type==vm_num? exp(val.num()):std::nan(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_lg(var* local, gc& ngc) {
|
var builtin_lg(context* ctx, gc* ngc) {
|
||||||
var val = local[1];
|
auto val = ctx->localr[1];
|
||||||
return var::num(val.type==vm_num? log(val.num())/log(10.0):std::nan(""));
|
return var::num(val.type==vm_num? log(val.num())/log(10.0):std::nan(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_ln(var* local, gc& ngc) {
|
var builtin_ln(context* ctx, gc* ngc) {
|
||||||
var val = local[1];
|
auto val = ctx->localr[1];
|
||||||
return var::num(val.type==vm_num? log(val.num()):std::nan(""));
|
return var::num(val.type==vm_num? log(val.num()):std::nan(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_sqrt(var* local, gc& ngc) {
|
var builtin_sqrt(context* ctx, gc* ngc) {
|
||||||
var val = local[1];
|
auto val = ctx->localr[1];
|
||||||
return var::num(val.type==vm_num? sqrt(val.num()):std::nan(""));
|
return var::num(val.type==vm_num? sqrt(val.num()):std::nan(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_atan2(var* local, gc& ngc) {
|
var builtin_atan2(context* ctx, gc* ngc) {
|
||||||
var x = local[1];
|
auto x = ctx->localr[1];
|
||||||
var y = local[2];
|
auto y = ctx->localr[2];
|
||||||
if (x.type!=vm_num || y.type!=vm_num) {
|
if (x.type!=vm_num || y.type!=vm_num) {
|
||||||
return var::num(std::nan(""));
|
return var::num(std::nan(""));
|
||||||
}
|
}
|
||||||
return var::num(atan2(y.num(), x.num()));
|
return var::num(atan2(y.num(), x.num()));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_isnan(var* local, gc& ngc) {
|
var builtin_isnan(context* ctx, gc* ngc) {
|
||||||
var x = local[1];
|
auto x = ctx->localr[1];
|
||||||
return (x.type==vm_num && std::isnan(x.num()))?one:zero;
|
return (x.type==vm_num && std::isnan(x.num()))? one:zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
nasal_builtin_table math_lib_native[] = {
|
nasal_builtin_table math_lib_native[] = {
|
||||||
|
|
|
@ -6,16 +6,16 @@
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
||||||
var builtin_pow(var*, gc&);
|
var builtin_pow(context*, gc*);
|
||||||
var builtin_sin(var*, gc&);
|
var builtin_sin(context*, gc*);
|
||||||
var builtin_cos(var*, gc&);
|
var builtin_cos(context*, gc*);
|
||||||
var builtin_tan(var*, gc&);
|
var builtin_tan(context*, gc*);
|
||||||
var builtin_exp(var*, gc&);
|
var builtin_exp(context*, gc*);
|
||||||
var builtin_lg(var*, gc&);
|
var builtin_lg(context*, gc*);
|
||||||
var builtin_ln(var*, gc&);
|
var builtin_ln(context*, gc*);
|
||||||
var builtin_sqrt(var*, gc&);
|
var builtin_sqrt(context*, gc*);
|
||||||
var builtin_atan2(var*, gc&);
|
var builtin_atan2(context*, gc*);
|
||||||
var builtin_isnan(var*, gc&);
|
var builtin_isnan(context*, gc*);
|
||||||
|
|
||||||
extern nasal_builtin_table math_lib_native[];
|
extern nasal_builtin_table math_lib_native[];
|
||||||
|
|
||||||
|
|
13
src/nasal.h
13
src/nasal.h
|
@ -1,15 +1,15 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef __nasver
|
#ifndef __nasver
|
||||||
#define __nasver "11.0"
|
#define __nasver "11.1"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <sstream>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
// abbreviation of some useful basic type
|
// abbreviation of some useful basic type
|
||||||
using i32 = std::int32_t;
|
using i32 = std::int32_t;
|
||||||
|
@ -36,8 +36,7 @@ bool is_powerpc();
|
||||||
bool is_superh();
|
bool is_superh();
|
||||||
|
|
||||||
|
|
||||||
// virtual machine stack depth
|
// virtual machine stack depth, both global depth and value stack depth
|
||||||
// both global depth and value stack depth
|
|
||||||
const u32 STACK_DEPTH = 4096;
|
const u32 STACK_DEPTH = 4096;
|
||||||
|
|
||||||
f64 hex2f(const char*);
|
f64 hex2f(const char*);
|
||||||
|
@ -54,8 +53,6 @@ f64 dec2f(const char*);
|
||||||
f64 str2num(const char*);
|
f64 str2num(const char*);
|
||||||
i32 utf8_hdchk(const char);
|
i32 utf8_hdchk(const char);
|
||||||
std::string chrhex(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"
|
|
|
@ -77,7 +77,7 @@ public:
|
||||||
virtual void accept(ast_visitor*);
|
virtual void accept(ast_visitor*);
|
||||||
};
|
};
|
||||||
|
|
||||||
class call:public expr {
|
class call: public expr {
|
||||||
public:
|
public:
|
||||||
call(const span& location, expr_type node_type):
|
call(const span& location, expr_type node_type):
|
||||||
expr(location, node_type) {}
|
expr(location, node_type) {}
|
||||||
|
@ -85,7 +85,7 @@ public:
|
||||||
virtual void accept(ast_visitor*);
|
virtual void accept(ast_visitor*);
|
||||||
};
|
};
|
||||||
|
|
||||||
class null_expr:public expr {
|
class null_expr: public expr {
|
||||||
public:
|
public:
|
||||||
null_expr(const span& location):
|
null_expr(const span& location):
|
||||||
expr(location, expr_type::ast_null) {}
|
expr(location, expr_type::ast_null) {}
|
||||||
|
@ -93,7 +93,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class nil_expr:public expr {
|
class nil_expr: public expr {
|
||||||
public:
|
public:
|
||||||
nil_expr(const span& location):
|
nil_expr(const span& location):
|
||||||
expr(location, expr_type::ast_nil) {}
|
expr(location, expr_type::ast_nil) {}
|
||||||
|
@ -101,7 +101,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class number_literal:public expr {
|
class number_literal: public expr {
|
||||||
private:
|
private:
|
||||||
f64 number;
|
f64 number;
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class string_literal:public expr {
|
class string_literal: public expr {
|
||||||
private:
|
private:
|
||||||
std::string content;
|
std::string content;
|
||||||
|
|
||||||
|
@ -125,7 +125,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class identifier:public expr {
|
class identifier: public expr {
|
||||||
private:
|
private:
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class bool_literal:public expr {
|
class bool_literal: public expr {
|
||||||
private:
|
private:
|
||||||
bool flag;
|
bool flag;
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class vector_expr:public expr {
|
class vector_expr: public expr {
|
||||||
private:
|
private:
|
||||||
std::vector<expr*> elements;
|
std::vector<expr*> elements;
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class hash_expr:public expr {
|
class hash_expr: public expr {
|
||||||
private:
|
private:
|
||||||
std::vector<hash_pair*> members;
|
std::vector<hash_pair*> members;
|
||||||
|
|
||||||
|
@ -175,7 +175,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class hash_pair:public expr {
|
class hash_pair: public expr {
|
||||||
private:
|
private:
|
||||||
std::string name;
|
std::string name;
|
||||||
expr* value;
|
expr* value;
|
||||||
|
@ -192,7 +192,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class function:public expr {
|
class function: public expr {
|
||||||
private:
|
private:
|
||||||
std::vector<parameter*> parameter_list;
|
std::vector<parameter*> parameter_list;
|
||||||
code_block* block;
|
code_block* block;
|
||||||
|
@ -209,7 +209,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class code_block:public expr {
|
class code_block: public expr {
|
||||||
private:
|
private:
|
||||||
std::vector<expr*> expressions;
|
std::vector<expr*> expressions;
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class parameter:public expr {
|
class parameter: public expr {
|
||||||
public:
|
public:
|
||||||
enum class param_type {
|
enum class param_type {
|
||||||
normal_parameter,
|
normal_parameter,
|
||||||
|
@ -249,7 +249,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ternary_operator:public expr {
|
class ternary_operator: public expr {
|
||||||
private:
|
private:
|
||||||
expr* condition;
|
expr* condition;
|
||||||
expr* left;
|
expr* left;
|
||||||
|
@ -269,7 +269,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class binary_operator:public expr {
|
class binary_operator: public expr {
|
||||||
public:
|
public:
|
||||||
enum class binary_type {
|
enum class binary_type {
|
||||||
add,
|
add,
|
||||||
|
@ -317,7 +317,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class unary_operator:public expr {
|
class unary_operator: public expr {
|
||||||
public:
|
public:
|
||||||
enum class unary_type {
|
enum class unary_type {
|
||||||
negative,
|
negative,
|
||||||
|
@ -344,7 +344,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class call_expr:public expr {
|
class call_expr: public expr {
|
||||||
private:
|
private:
|
||||||
expr* first;
|
expr* first;
|
||||||
std::vector<call*> calls;
|
std::vector<call*> calls;
|
||||||
|
@ -361,7 +361,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class call_hash:public call {
|
class call_hash: public call {
|
||||||
private:
|
private:
|
||||||
std::string field;
|
std::string field;
|
||||||
|
|
||||||
|
@ -374,7 +374,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class call_vector:public call {
|
class call_vector: public call {
|
||||||
private:
|
private:
|
||||||
std::vector<slice_vector*> calls;
|
std::vector<slice_vector*> calls;
|
||||||
|
|
||||||
|
@ -387,7 +387,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class call_function:public call {
|
class call_function: public call {
|
||||||
private:
|
private:
|
||||||
std::vector<expr*> args;
|
std::vector<expr*> args;
|
||||||
|
|
||||||
|
@ -400,7 +400,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class slice_vector:public expr {
|
class slice_vector: public expr {
|
||||||
private:
|
private:
|
||||||
expr* begin;
|
expr* begin;
|
||||||
expr* end;
|
expr* end;
|
||||||
|
@ -417,7 +417,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class definition_expr:public expr {
|
class definition_expr: public expr {
|
||||||
private:
|
private:
|
||||||
identifier* variable_name;
|
identifier* variable_name;
|
||||||
multi_identifier* variables;
|
multi_identifier* variables;
|
||||||
|
@ -441,7 +441,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class assignment_expr:public expr {
|
class assignment_expr: public expr {
|
||||||
public:
|
public:
|
||||||
enum class assign_type {
|
enum class assign_type {
|
||||||
equal,
|
equal,
|
||||||
|
@ -474,7 +474,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class multi_identifier:public expr {
|
class multi_identifier: public expr {
|
||||||
private:
|
private:
|
||||||
std::vector<identifier*> variables;
|
std::vector<identifier*> variables;
|
||||||
|
|
||||||
|
@ -487,7 +487,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class tuple_expr:public expr {
|
class tuple_expr: public expr {
|
||||||
private:
|
private:
|
||||||
std::vector<expr*> elements;
|
std::vector<expr*> elements;
|
||||||
|
|
||||||
|
@ -500,7 +500,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class multi_assign:public expr {
|
class multi_assign: public expr {
|
||||||
private:
|
private:
|
||||||
tuple_expr* tuple;
|
tuple_expr* tuple;
|
||||||
expr* value;
|
expr* value;
|
||||||
|
@ -517,7 +517,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class while_expr:public expr {
|
class while_expr: public expr {
|
||||||
private:
|
private:
|
||||||
expr* condition;
|
expr* condition;
|
||||||
code_block* block;
|
code_block* block;
|
||||||
|
@ -534,7 +534,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class for_expr:public expr {
|
class for_expr: public expr {
|
||||||
private:
|
private:
|
||||||
expr* initializing;
|
expr* initializing;
|
||||||
expr* condition;
|
expr* condition;
|
||||||
|
@ -558,24 +558,28 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class iter_expr:public expr {
|
class iter_expr: public expr {
|
||||||
private:
|
private:
|
||||||
|
bool is_iterator_definition;
|
||||||
identifier* name;
|
identifier* name;
|
||||||
call_expr* call;
|
call_expr* call;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
iter_expr(const span& location):
|
iter_expr(const span& location):
|
||||||
expr(location, expr_type::ast_iter),
|
expr(location, expr_type::ast_iter),
|
||||||
|
is_iterator_definition(false),
|
||||||
name(nullptr), call(nullptr) {}
|
name(nullptr), call(nullptr) {}
|
||||||
~iter_expr() override;
|
~iter_expr() override;
|
||||||
void set_name(identifier* node) {name = node;}
|
void set_name(identifier* node) {name = node;}
|
||||||
void set_call(call_expr* node) {call = node;}
|
void set_call(call_expr* node) {call = node;}
|
||||||
|
void set_is_definition(bool flag) {is_iterator_definition = flag;}
|
||||||
identifier* get_name() {return name;}
|
identifier* get_name() {return name;}
|
||||||
call_expr* get_call() {return call;}
|
call_expr* get_call() {return call;}
|
||||||
|
bool is_definition() const {return is_iterator_definition;}
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class forei_expr:public expr {
|
class forei_expr: public expr {
|
||||||
public:
|
public:
|
||||||
enum class forei_loop_type {
|
enum class forei_loop_type {
|
||||||
foreach,
|
foreach,
|
||||||
|
@ -605,7 +609,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class condition_expr:public expr {
|
class condition_expr: public expr {
|
||||||
private:
|
private:
|
||||||
if_expr* if_stmt;
|
if_expr* if_stmt;
|
||||||
std::vector<if_expr*> elsif_stmt;
|
std::vector<if_expr*> elsif_stmt;
|
||||||
|
@ -625,7 +629,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class if_expr:public expr {
|
class if_expr: public expr {
|
||||||
private:
|
private:
|
||||||
expr* condition;
|
expr* condition;
|
||||||
code_block* block;
|
code_block* block;
|
||||||
|
@ -642,7 +646,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class continue_expr:public expr {
|
class continue_expr: public expr {
|
||||||
public:
|
public:
|
||||||
continue_expr(const span& location):
|
continue_expr(const span& location):
|
||||||
expr(location, expr_type::ast_continue) {}
|
expr(location, expr_type::ast_continue) {}
|
||||||
|
@ -650,7 +654,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class break_expr:public expr {
|
class break_expr: public expr {
|
||||||
public:
|
public:
|
||||||
break_expr(const span& location):
|
break_expr(const span& location):
|
||||||
expr(location, expr_type::ast_break) {}
|
expr(location, expr_type::ast_break) {}
|
||||||
|
@ -658,7 +662,7 @@ public:
|
||||||
void accept(ast_visitor*) override;
|
void accept(ast_visitor*) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class return_expr:public expr {
|
class return_expr: public expr {
|
||||||
private:
|
private:
|
||||||
expr* value;
|
expr* value;
|
||||||
|
|
||||||
|
|
|
@ -3,33 +3,34 @@
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
||||||
var builtin_print(var* local, gc& ngc) {
|
var builtin_print(context* ctx, gc* ngc) {
|
||||||
for(auto& i : local[1].vec().elems) {
|
for(auto& i : ctx->localr[1].vec().elems) {
|
||||||
std::cout << i;
|
std::cout << i;
|
||||||
}
|
}
|
||||||
std::cout << std::flush;
|
std::cout << std::flush;
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_println(var* local, gc& ngc) {
|
var builtin_println(context* ctx, gc* ngc) {
|
||||||
for(auto& i : local[1].vec().elems) {
|
for(auto& i : ctx->localr[1].vec().elems) {
|
||||||
std::cout << i;
|
std::cout << i;
|
||||||
}
|
}
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_exit(var* local, gc& ngc) {
|
var builtin_exit(context* ctx, gc* ngc) {
|
||||||
std::exit(local[1].num());
|
std::exit(ctx->localr[1].num());
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_abort(var* local, gc& ngc) {
|
var builtin_abort(context* ctx, gc* ngc) {
|
||||||
std::abort();
|
std::abort();
|
||||||
return nil;
|
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 vec = local[1];
|
||||||
var elem = local[2];
|
var elem = local[2];
|
||||||
if (vec.type!=vm_vec) {
|
if (vec.type!=vm_vec) {
|
||||||
|
@ -42,7 +43,8 @@ var builtin_append(var* local, gc& ngc) {
|
||||||
return nil;
|
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 vec = local[1];
|
||||||
var size = local[2];
|
var size = local[2];
|
||||||
if (vec.type!=vm_vec) {
|
if (vec.type!=vm_vec) {
|
||||||
|
@ -55,17 +57,18 @@ var builtin_setsize(var* local, gc& ngc) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_system(var* local, gc& ngc) {
|
var builtin_system(context* ctx, gc* ngc) {
|
||||||
var str = local[1];
|
auto str = ctx->localr[1];
|
||||||
if (str.type!=vm_str) {
|
if (str.type!=vm_str) {
|
||||||
return var::num(-1);
|
return var::num(-1);
|
||||||
}
|
}
|
||||||
return var::num(static_cast<f64>(system(str.str().c_str())));
|
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 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()) {
|
if (end.type!=vm_str || end.str().length()>1 || !end.str().length()) {
|
||||||
std::cin >> ret.str();
|
std::cin >> ret.str();
|
||||||
} else {
|
} else {
|
||||||
|
@ -74,7 +77,8 @@ var builtin_input(var* local, gc& ngc) {
|
||||||
return ret;
|
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 delimeter = local[1];
|
||||||
var str = local[2];
|
var str = local[2];
|
||||||
if (delimeter.type!=vm_str) {
|
if (delimeter.type!=vm_str) {
|
||||||
|
@ -87,34 +91,34 @@ var builtin_split(var* local, gc& ngc) {
|
||||||
const auto& s = str.str();
|
const auto& s = str.str();
|
||||||
|
|
||||||
// avoid being sweeped
|
// avoid being sweeped
|
||||||
var res = ngc.temp = ngc.alloc(vm_vec);
|
auto res = ngc->temp = ngc->alloc(vm_vec);
|
||||||
auto& vec = res.vec().elems;
|
auto& vec = res.vec().elems;
|
||||||
|
|
||||||
if (!deli.length()) {
|
if (!deli.length()) {
|
||||||
for(auto i : s) {
|
for(auto i : s) {
|
||||||
vec.push_back(ngc.newstr(i));
|
vec.push_back(ngc->newstr(i));
|
||||||
}
|
}
|
||||||
ngc.temp = nil;
|
ngc->temp = nil;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
usize last = 0;
|
usize last = 0;
|
||||||
usize pos = s.find(deli, 0);
|
usize pos = s.find(deli, 0);
|
||||||
while(pos!=std::string::npos) {
|
while(pos!=std::string::npos) {
|
||||||
if (pos>last) {
|
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();
|
last = pos+deli.length();
|
||||||
pos = s.find(deli, last);
|
pos = s.find(deli, last);
|
||||||
}
|
}
|
||||||
if (last!=s.length()) {
|
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;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_rand(var* local, gc& ngc) {
|
var builtin_rand(context* ctx, gc* ngc) {
|
||||||
var val = local[1];
|
auto val = ctx->localr[1];
|
||||||
if (val.type!=vm_num && val.type!=vm_nil) {
|
if (val.type!=vm_num && val.type!=vm_nil) {
|
||||||
return nas_err("rand", "\"seed\" must be nil or number");
|
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);
|
return var::num(num);
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_id(var* local, gc& ngc) {
|
var builtin_id(context* ctx, gc* ngc) {
|
||||||
var val = local[1];
|
auto val = ctx->localr[1];
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << "0";
|
ss << "0";
|
||||||
if (val.type>vm_num) {
|
if (val.type>vm_num) {
|
||||||
ss << "x" << std::hex;
|
ss << "x" << std::hex;
|
||||||
ss << reinterpret_cast<u64>(val.val.gcobj) << std::dec;
|
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 builtin_int(context* ctx, gc* ngc) {
|
||||||
var val = local[1];
|
auto val = ctx->localr[1];
|
||||||
if (val.type!=vm_num && val.type!=vm_str) {
|
if (val.type!=vm_num && val.type!=vm_str) {
|
||||||
return nil;
|
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 builtin_floor(context* ctx, gc* ngc) {
|
||||||
var val = local[1];
|
auto value = ctx->localr[1];
|
||||||
return var::num(std::floor(val.num()));
|
return var::num(std::floor(value.num()));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_num(var* local, gc& ngc) {
|
var builtin_ceil(context* ctx, gc* ngc) {
|
||||||
var val = local[1];
|
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) {
|
if (val.type==vm_num) {
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
if (val.type!=vm_str) {
|
if (val.type!=vm_str) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
f64 res = val.tonum();
|
auto res = val.to_num();
|
||||||
if (std::isnan(res)) {
|
if (std::isnan(res)) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
return var::num(res);
|
return var::num(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_pop(var* local, gc& ngc) {
|
var builtin_pop(context* ctx, gc* ngc) {
|
||||||
var val = local[1];
|
auto val = ctx->localr[1];
|
||||||
if (val.type!=vm_vec) {
|
if (val.type!=vm_vec) {
|
||||||
return nas_err("pop", "\"vec\" must be vector");
|
return nas_err("pop", "\"vec\" must be vector");
|
||||||
}
|
}
|
||||||
auto& vec = val.vec().elems;
|
auto& vec = val.vec().elems;
|
||||||
if (vec.size()) {
|
if (vec.size()) {
|
||||||
var tmp = vec.back();
|
auto tmp = vec.back();
|
||||||
vec.pop_back();
|
vec.pop_back();
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_str(var* local, gc& ngc) {
|
var builtin_str(context* ctx, gc* ngc) {
|
||||||
return ngc.newstr(local[1].tostr());
|
return ngc->newstr(ctx->localr[1].to_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_size(var* local, gc& ngc) {
|
var builtin_size(context* ctx, gc* ngc) {
|
||||||
var val = local[1];
|
auto val = ctx->localr[1];
|
||||||
f64 num = 0;
|
f64 num = 0;
|
||||||
switch(val.type) {
|
switch(val.type) {
|
||||||
case vm_num: num = val.num(); break;
|
case vm_num: num = val.num(); break;
|
||||||
|
@ -199,16 +208,17 @@ var builtin_size(var* local, gc& ngc) {
|
||||||
return var::num(num);
|
return var::num(num);
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_time(var* local, gc& ngc) {
|
var builtin_time(context* ctx, gc* ngc) {
|
||||||
var val = local[1];
|
auto val = ctx->localr[1];
|
||||||
if (val.type!=vm_num) {
|
if (val.type!=vm_num) {
|
||||||
return nas_err("time", "\"begin\" must be number");
|
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)));
|
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 hash = local[1];
|
||||||
var key = local[2];
|
var key = local[2];
|
||||||
if (hash.type!=vm_hash || key.type!=vm_str) {
|
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;
|
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 hash = local[1];
|
||||||
var key = local[2];
|
var key = local[2];
|
||||||
if (hash.type!=vm_hash) {
|
if (hash.type!=vm_hash) {
|
||||||
|
@ -232,58 +243,60 @@ var builtin_delete(var* local, gc& ngc) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_keys(var* local, gc& ngc) {
|
var builtin_keys(context* ctx, gc* ngc) {
|
||||||
var hash = local[1];
|
auto hash = ctx->localr[1];
|
||||||
if (hash.type!=vm_hash && hash.type!=vm_map) {
|
if (hash.type!=vm_hash && hash.type!=vm_map) {
|
||||||
return nas_err("keys", "\"hash\" must be hash");
|
return nas_err("keys", "\"hash\" must be hash");
|
||||||
}
|
}
|
||||||
// avoid being sweeped
|
// avoid being sweeped
|
||||||
var res = ngc.temp = ngc.alloc(vm_vec);
|
auto res = ngc->temp = ngc->alloc(vm_vec);
|
||||||
auto& vec = res.vec().elems;
|
auto& vec = res.vec().elems;
|
||||||
if (hash.type==vm_hash) {
|
if (hash.type==vm_hash) {
|
||||||
for(const auto& iter : hash.hash().elems) {
|
for(const auto& iter : hash.hash().elems) {
|
||||||
vec.push_back(ngc.newstr(iter.first));
|
vec.push_back(ngc->newstr(iter.first));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for(const auto& iter : hash.map().mapper) {
|
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;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_die(var* local, gc& ngc) {
|
var builtin_die(context* ctx, gc* ngc) {
|
||||||
return nas_err("error", local[1].tostr());
|
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 needle = local[1];
|
||||||
var haystack = local[2];
|
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) {
|
if (pos==std::string::npos) {
|
||||||
return var::num(-1.0);
|
return var::num(-1.0);
|
||||||
}
|
}
|
||||||
return var::num(static_cast<f64>(pos));
|
return var::num(static_cast<f64>(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_type(var* local, gc& ngc) {
|
var builtin_type(context* ctx, gc* ngc) {
|
||||||
switch(local[1].type) {
|
switch(ctx->localr[1].type) {
|
||||||
case vm_none: return ngc.newstr("undefined");
|
case vm_none: return ngc->newstr("undefined");
|
||||||
case vm_nil: return ngc.newstr("nil");
|
case vm_nil: return ngc->newstr("nil");
|
||||||
case vm_num: return ngc.newstr("num");
|
case vm_num: return ngc->newstr("num");
|
||||||
case vm_str: return ngc.newstr("str");
|
case vm_str: return ngc->newstr("str");
|
||||||
case vm_vec: return ngc.newstr("vec");
|
case vm_vec: return ngc->newstr("vec");
|
||||||
case vm_hash: return ngc.newstr("hash");
|
case vm_hash: return ngc->newstr("hash");
|
||||||
case vm_func: return ngc.newstr("func");
|
case vm_func: return ngc->newstr("func");
|
||||||
case vm_obj: return ngc.newstr("obj");
|
case vm_obj: return ngc->newstr("obj");
|
||||||
case vm_co: return ngc.newstr("coroutine");
|
case vm_co: return ngc->newstr("coroutine");
|
||||||
case vm_map: return ngc.newstr("namespace");
|
case vm_map: return ngc->newstr("namespace");
|
||||||
}
|
}
|
||||||
return nil;
|
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 str = local[1];
|
||||||
var beg = local[2];
|
var beg = local[2];
|
||||||
var len = local[3];
|
var len = local[3];
|
||||||
|
@ -301,10 +314,11 @@ var builtin_substr(var* local, gc& ngc) {
|
||||||
if (begin>=str.str().length()) {
|
if (begin>=str.str().length()) {
|
||||||
return nas_err("susbtr", "begin index out of range: "+std::to_string(begin));
|
return nas_err("susbtr", "begin index out of range: "+std::to_string(begin));
|
||||||
}
|
}
|
||||||
return ngc.newstr(str.str().substr(begin,length));
|
return ngc->newstr(str.str().substr(begin,length));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_streq(var* local, gc& ngc) {
|
var builtin_streq(context* ctx, gc* ngc) {
|
||||||
|
auto local = ctx->localr;
|
||||||
var a = local[1];
|
var a = local[1];
|
||||||
var b = local[2];
|
var b = local[2];
|
||||||
return var::num(static_cast<f64>(
|
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 str = local[1];
|
||||||
var len = local[2];
|
var len = local[2];
|
||||||
if (str.type!=vm_str) {
|
if (str.type!=vm_str) {
|
||||||
|
@ -322,12 +337,13 @@ var builtin_left(var* local, gc& ngc) {
|
||||||
return nas_err("left", "\"length\" must be number");
|
return nas_err("left", "\"length\" must be number");
|
||||||
}
|
}
|
||||||
if (len.num()<0) {
|
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 str = local[1];
|
||||||
var len = local[2];
|
var len = local[2];
|
||||||
if (str.type!=vm_str) {
|
if (str.type!=vm_str) {
|
||||||
|
@ -344,10 +360,11 @@ var builtin_right(var* local, gc& ngc) {
|
||||||
if (length<0) {
|
if (length<0) {
|
||||||
length = 0;
|
length = 0;
|
||||||
}
|
}
|
||||||
return ngc.newstr(str.str().substr(srclen-length, srclen));
|
return ngc->newstr(str.str().substr(srclen-length, srclen));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_cmp(var* local, gc& ngc) {
|
var builtin_cmp(context* ctx, gc* ngc) {
|
||||||
|
auto local = ctx->localr;
|
||||||
var a = local[1];
|
var a = local[1];
|
||||||
var b = local[2];
|
var b = local[2];
|
||||||
if (a.type!=vm_str || b.type!=vm_str) {
|
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[] = {
|
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) {
|
if (0<=num && num<128) {
|
||||||
return ngc.newstr((char)num);
|
return ngc->newstr((char)num);
|
||||||
} else if (128<=num && num<256) {
|
} 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) {
|
var builtin_char(context* ctx, gc* ngc) {
|
||||||
return ngc.newstr((unsigned char)local[1].num());
|
return ngc->newstr(static_cast<unsigned char>(ctx->localr[1].num()));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_values(var* local, gc& ngc) {
|
var builtin_values(context* ctx, gc* ngc) {
|
||||||
var hash = local[1];
|
auto hash = ctx->localr[1];
|
||||||
if (hash.type!=vm_hash) {
|
if (hash.type!=vm_hash && hash.type!=vm_map) {
|
||||||
return nas_err("values", "\"hash\" must be hash");
|
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;
|
auto& v = vec.vec().elems;
|
||||||
for(auto& i : hash.hash().elems) {
|
if (hash.type==vm_hash) {
|
||||||
v.push_back(i.second);
|
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;
|
return vec;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_sleep(var* local, gc& ngc) {
|
var builtin_sleep(context* ctx, gc* ngc) {
|
||||||
var val = local[1];
|
auto val = ctx->localr[1];
|
||||||
if (val.type!=vm_num) {
|
if (val.type!=vm_num) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
@ -421,36 +444,36 @@ var builtin_sleep(var* local, gc& ngc) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_platform(var* local, gc& ngc) {
|
var builtin_platform(context* ctx, gc* ngc) {
|
||||||
if (is_windows()) {
|
if (is_windows()) {
|
||||||
return ngc.newstr("windows");
|
return ngc->newstr("windows");
|
||||||
} else if (is_linux()) {
|
} else if (is_linux()) {
|
||||||
return ngc.newstr("linux");
|
return ngc->newstr("linux");
|
||||||
} else if (is_macos()) {
|
} 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()) {
|
if (is_x86()) {
|
||||||
return ngc.newstr("x86");
|
return ngc->newstr("x86");
|
||||||
} else if (is_x86_64()) {
|
} else if (is_x86_64()) {
|
||||||
return ngc.newstr("x86-64");
|
return ngc->newstr("x86-64");
|
||||||
} else if (is_amd64()) {
|
} else if (is_amd64()) {
|
||||||
return ngc.newstr("amd64");
|
return ngc->newstr("amd64");
|
||||||
} else if (is_arm()) {
|
} else if (is_arm()) {
|
||||||
return ngc.newstr("arm");
|
return ngc->newstr("arm");
|
||||||
} else if (is_aarch64()) {
|
} else if (is_aarch64()) {
|
||||||
return ngc.newstr("aarch64");
|
return ngc->newstr("aarch64");
|
||||||
} else if (is_ia64()) {
|
} else if (is_ia64()) {
|
||||||
return ngc.newstr("ia64");
|
return ngc->newstr("ia64");
|
||||||
} else if (is_powerpc()) {
|
} else if (is_powerpc()) {
|
||||||
return ngc.newstr("powerpc");
|
return ngc->newstr("powerpc");
|
||||||
} else if (is_superh()) {
|
} else if (is_superh()) {
|
||||||
return ngc.newstr("superh");
|
return ngc->newstr("superh");
|
||||||
}
|
}
|
||||||
return ngc.newstr("unknown");
|
return ngc->newstr("unknown");
|
||||||
}
|
}
|
||||||
|
|
||||||
// md5 related functions
|
// md5 related functions
|
||||||
|
@ -536,64 +559,64 @@ std::string md5(const std::string& src) {
|
||||||
return tohex(atmp)+tohex(btmp)+tohex(ctmp)+tohex(dtmp);
|
return tohex(atmp)+tohex(btmp)+tohex(ctmp)+tohex(dtmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_md5(var* local, gc& ngc) {
|
var builtin_md5(context* ctx, gc* ngc) {
|
||||||
var str = local[1];
|
auto str = ctx->localr[1];
|
||||||
if (str.type!=vm_str) {
|
if (str.type!=vm_str) {
|
||||||
return nas_err("md5", "\"str\" must be string");
|
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>
|
f64 res = std::chrono::duration_cast<std::chrono::milliseconds>
|
||||||
(std::chrono::high_resolution_clock::now().time_since_epoch())
|
(std::chrono::high_resolution_clock::now().time_since_epoch())
|
||||||
.count();
|
.count();
|
||||||
return var::num(res);
|
return var::num(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_gcextend(var* local, gc& ngc) {
|
var builtin_gcextend(context* ctx, gc* ngc) {
|
||||||
var type = local[1];
|
auto type = ctx->localr[1];
|
||||||
if (type.type!=vm_str) {
|
if (type.type!=vm_str) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
auto& s = type.str();
|
const auto& s = type.str();
|
||||||
if (s=="str") {
|
if (s=="str") {
|
||||||
ngc.extend(vm_str);
|
ngc->extend(vm_str);
|
||||||
} else if (s=="vec") {
|
} else if (s=="vec") {
|
||||||
ngc.extend(vm_vec);
|
ngc->extend(vm_vec);
|
||||||
} else if (s=="hash") {
|
} else if (s=="hash") {
|
||||||
ngc.extend(vm_hash);
|
ngc->extend(vm_hash);
|
||||||
} else if (s=="func") {
|
} else if (s=="func") {
|
||||||
ngc.extend(vm_func);
|
ngc->extend(vm_func);
|
||||||
} else if (s=="upval") {
|
} else if (s=="upval") {
|
||||||
ngc.extend(vm_upval);
|
ngc->extend(vm_upval);
|
||||||
} else if (s=="obj") {
|
} else if (s=="obj") {
|
||||||
ngc.extend(vm_obj);
|
ngc->extend(vm_obj);
|
||||||
} else if (s=="co") {
|
} else if (s=="co") {
|
||||||
ngc.extend(vm_co);
|
ngc->extend(vm_co);
|
||||||
}
|
}
|
||||||
return nil;
|
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;
|
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;
|
double total = 0;
|
||||||
for(u32 i = 0; i<gc_type_size; ++i) {
|
for(u32 i = 0; i<gc_type_size; ++i) {
|
||||||
total += ngc.gcnt[i];
|
total += ngc->gcnt[i];
|
||||||
}
|
}
|
||||||
// using ms
|
// using ms
|
||||||
auto& map = res.hash().elems;
|
auto& map = res.hash().elems;
|
||||||
map["total"] = var::num(ngc.worktime*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["average"] = var::num(ngc->worktime*1.0/den*1000/total);
|
||||||
map["max_gc"] = var::num(ngc.max_time*1.0/den*1000);
|
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_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["max_sweep"] = var::num(ngc->max_sweep_time*1.0/den*1000);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_logtime(var* local, gc& ngc) {
|
var builtin_logtime(context* ctx, gc* ngc) {
|
||||||
time_t t = time(nullptr);
|
time_t t = time(nullptr);
|
||||||
tm* tm_t = localtime(&t);
|
tm* tm_t = localtime(&t);
|
||||||
char s[64];
|
char s[64];
|
||||||
|
@ -606,19 +629,19 @@ var builtin_logtime(var* local, gc& ngc) {
|
||||||
tm_t->tm_min,
|
tm_t->tm_min,
|
||||||
tm_t->tm_sec
|
tm_t->tm_sec
|
||||||
);
|
);
|
||||||
return ngc.newstr(s);
|
return ngc->newstr(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_ghosttype(var* local, gc& ngc) {
|
var builtin_ghosttype(context* ctx, gc* ngc) {
|
||||||
var arg = local[1];
|
auto arg = ctx->localr[1];
|
||||||
if (arg.type!=vm_obj) {
|
if (arg.type!=vm_obj) {
|
||||||
return nas_err("ghosttype", "this is not a ghost object.");
|
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()) {
|
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[] = {
|
nasal_builtin_table builtin[] = {
|
||||||
|
@ -635,6 +658,7 @@ nasal_builtin_table builtin[] = {
|
||||||
{"__id", builtin_id},
|
{"__id", builtin_id},
|
||||||
{"__int", builtin_int},
|
{"__int", builtin_int},
|
||||||
{"__floor", builtin_floor},
|
{"__floor", builtin_floor},
|
||||||
|
{"__ceil", builtin_ceil},
|
||||||
{"__num", builtin_num},
|
{"__num", builtin_num},
|
||||||
{"__pop", builtin_pop},
|
{"__pop", builtin_pop},
|
||||||
{"__str", builtin_str},
|
{"__str", builtin_str},
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "nasal.h"
|
#include "nasal.h"
|
||||||
|
#include "nasal_type.h"
|
||||||
#include "nasal_gc.h"
|
#include "nasal_gc.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning (disable:4566) // i know i'm using utf-8, fuck you
|
#pragma warning (disable:4566) // i know i'm using utf-8, fuck you
|
||||||
#pragma warning (disable:4244)
|
#pragma warning (disable:4244)
|
||||||
|
@ -24,56 +29,57 @@
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
||||||
var builtin_print(var*, gc&);
|
var builtin_print(context*, gc*);
|
||||||
var builtin_println(var*, gc&);
|
var builtin_println(context*, gc*);
|
||||||
var builtin_exit(var*, gc&);
|
var builtin_exit(context*, gc*);
|
||||||
var builtin_abort(var*, gc&);
|
var builtin_abort(context*, gc*);
|
||||||
var builtin_append(var*, gc&);
|
var builtin_append(context*, gc*);
|
||||||
var builtin_setsize(var*, gc&);
|
var builtin_setsize(context*, gc*);
|
||||||
var builtin_system(var*, gc&);
|
var builtin_system(context*, gc*);
|
||||||
var builtin_input(var*, gc&);
|
var builtin_input(context*, gc*);
|
||||||
var builtin_split(var*, gc&);
|
var builtin_split(context*, gc*);
|
||||||
var builtin_rand(var*, gc&);
|
var builtin_rand(context*, gc*);
|
||||||
var builtin_id(var*, gc&);
|
var builtin_id(context*, gc*);
|
||||||
var builtin_int(var*, gc&);
|
var builtin_int(context*, gc*);
|
||||||
var builtin_floor(var*, gc&);
|
var builtin_floor(context*, gc*);
|
||||||
var builtin_num(var*, gc&);
|
var builtin_ceil(context*, gc*);
|
||||||
var builtin_pop(var*, gc&);
|
var builtin_num(context*, gc*);
|
||||||
var builtin_str(var*, gc&);
|
var builtin_pop(context*, gc*);
|
||||||
var builtin_size(var*, gc&);
|
var builtin_str(context*, gc*);
|
||||||
var builtin_time(var*, gc&);
|
var builtin_size(context*, gc*);
|
||||||
var builtin_contains(var*, gc&);
|
var builtin_time(context*, gc*);
|
||||||
var builtin_delete(var*, gc&);
|
var builtin_contains(context*, gc*);
|
||||||
var builtin_keys(var*, gc&);
|
var builtin_delete(context*, gc*);
|
||||||
var builtin_die(var*, gc&);
|
var builtin_keys(context*, gc*);
|
||||||
var builtin_find(var*, gc&);
|
var builtin_die(context*, gc*);
|
||||||
var builtin_type(var*, gc&);
|
var builtin_find(context*, gc*);
|
||||||
var builtin_substr(var*, gc&);
|
var builtin_type(context*, gc*);
|
||||||
var builtin_streq(var*, gc&);
|
var builtin_substr(context*, gc*);
|
||||||
var builtin_left(var*, gc&);
|
var builtin_streq(context*, gc*);
|
||||||
var builtin_right(var*, gc&);
|
var builtin_left(context*, gc*);
|
||||||
var builtin_cmp(var*, gc&);
|
var builtin_right(context*, gc*);
|
||||||
var builtin_chr(var*, gc&);
|
var builtin_cmp(context*, gc*);
|
||||||
var builtin_char(var*, gc&);
|
var builtin_chr(context*, gc*);
|
||||||
var builtin_values(var*, gc&);
|
var builtin_char(context*, gc*);
|
||||||
var builtin_sleep(var*, gc&);
|
var builtin_values(context*, gc*);
|
||||||
var builtin_platform(var*, gc&);
|
var builtin_sleep(context*, gc*);
|
||||||
var builtin_arch(var*, gc&);
|
var builtin_platform(context*, gc*);
|
||||||
|
var builtin_arch(context*, gc*);
|
||||||
// md5 related functions
|
// md5 related functions
|
||||||
std::string tohex(u32);
|
std::string tohex(u32);
|
||||||
std::string md5(const std::string&);
|
std::string md5(const std::string&);
|
||||||
var builtin_md5(var*, gc&);
|
var builtin_md5(context*, gc*);
|
||||||
var builtin_millisec(var*, gc&);
|
var builtin_millisec(context*, gc*);
|
||||||
var builtin_gcextend(var*, gc&);
|
var builtin_gcextend(context*, gc*);
|
||||||
var builtin_gcinfo(var*, gc&);
|
var builtin_gcinfo(context*, gc*);
|
||||||
var builtin_logtime(var*, gc&);
|
var builtin_logtime(context*, gc*);
|
||||||
var builtin_ghosttype(var*, gc&);
|
var builtin_ghosttype(context*, gc*);
|
||||||
|
|
||||||
// register builtin function's name and it's address here in this table below
|
// register builtin function's name and it's address here in this table below
|
||||||
// this table must end with {nullptr,nullptr}
|
// this table must end with {nullptr,nullptr}
|
||||||
struct nasal_builtin_table {
|
struct nasal_builtin_table {
|
||||||
const char* name;
|
const char* name;
|
||||||
var (*func)(var*, gc&);
|
var (*func)(context*, gc*);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern nasal_builtin_table builtin[];
|
extern nasal_builtin_table builtin[];
|
||||||
|
|
|
@ -89,7 +89,7 @@ void codegen::find_symbol(code_block* node) {
|
||||||
experimental_namespace[i.location.file] = {};
|
experimental_namespace[i.location.file] = {};
|
||||||
}
|
}
|
||||||
// if in global scope, load global symbol into this namespace
|
// if in global scope, load global symbol into this namespace
|
||||||
auto scope = experimental_namespace.at(i.location.file);
|
auto& scope = experimental_namespace.at(i.location.file);
|
||||||
if (local.empty() && !scope.count(i.name)) {
|
if (local.empty() && !scope.count(i.name)) {
|
||||||
scope.insert(i.name);
|
scope.insert(i.name);
|
||||||
}
|
}
|
||||||
|
@ -835,13 +835,18 @@ void codegen::forei_gen(forei_expr* node) {
|
||||||
} else {
|
} else {
|
||||||
emit(op_feach, 0, node->get_location());
|
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
|
// 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();
|
const auto& str = name_node->get_name();
|
||||||
local.empty()?
|
local.empty()?
|
||||||
emit(op_loadg, global_symbol_find(str), name_node->get_location()):
|
emit(op_loadg, global_symbol_find(str), name_node->get_location()):
|
||||||
emit(op_loadl, local_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 {
|
} else {
|
||||||
// use exist variable as the iterator
|
// use exist variable as the iterator
|
||||||
mcall(node->get_iterator()->get_call());
|
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";
|
out << std::hex << "<0x" << func_begin_stack.top() << std::dec << ">;\n";
|
||||||
// avoid two empty lines
|
// avoid two empty lines
|
||||||
if (c.op!=op_newf) {
|
if (c.op!=op_newf) {
|
||||||
out<<"\n";
|
out << "\n";
|
||||||
}
|
}
|
||||||
func_begin_stack.pop();
|
func_begin_stack.pop();
|
||||||
func_end_stack.pop();
|
func_end_stack.pop();
|
||||||
|
@ -1322,7 +1327,7 @@ void codegen::print(std::ostream& out) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// output bytecode
|
// output bytecode
|
||||||
out << " " << codestream(c,i) << "\n";
|
out << " " << codestream(c, i) << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -159,8 +159,8 @@ void dbg::step_info() {
|
||||||
|
|
||||||
begin = (ctx.pc>>3)==0? 0:((ctx.pc>>3)<<3);
|
begin = (ctx.pc>>3)==0? 0:((ctx.pc>>3)<<3);
|
||||||
end = (1+(ctx.pc>>3))<<3;
|
end = (1+(ctx.pc>>3))<<3;
|
||||||
codestream::set(cnum, cstr, native.data(), files);
|
codestream::set(const_number, const_string, native_function.data(), files);
|
||||||
std::clog << "next bytecode:\n";
|
std::clog << "\nnext bytecode:\n";
|
||||||
for(u32 i = begin; i<end && bytecode[i].op!=op_exit; ++i) {
|
for(u32 i = begin; i<end && bytecode[i].op!=op_exit; ++i) {
|
||||||
std::clog
|
std::clog
|
||||||
<< (i==ctx.pc? back_white:reset)
|
<< (i==ctx.pc? back_white:reset)
|
||||||
|
@ -168,7 +168,7 @@ void dbg::step_info() {
|
||||||
<< codestream(bytecode[i], i)
|
<< codestream(bytecode[i], i)
|
||||||
<< reset << "\n";
|
<< reset << "\n";
|
||||||
}
|
}
|
||||||
stackinfo(10);
|
stack_info(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dbg::interact() {
|
void dbg::interact() {
|
||||||
|
@ -181,10 +181,10 @@ void dbg::interact() {
|
||||||
if (do_profiling) {
|
if (do_profiling) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((bytecode[ctx.pc].fidx!=bk_fidx ||
|
// is not break point and is not next stop command
|
||||||
bytecode[ctx.pc].line!=bk_line) && // break point
|
const auto& code = bytecode[ctx.pc];
|
||||||
!next) {// next step
|
if ((code.fidx!=break_file_index || code.line!=break_line) && !next) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,28 +194,31 @@ void dbg::interact() {
|
||||||
while(true) {
|
while(true) {
|
||||||
std::clog << ">> ";
|
std::clog << ">> ";
|
||||||
std::getline(std::cin, cmd);
|
std::getline(std::cin, cmd);
|
||||||
auto res=parse(cmd);
|
auto res = parse(cmd);
|
||||||
if (res.size()==0) {
|
if (res.size()==0) {
|
||||||
step_info();
|
step_info();
|
||||||
} else if (res.size()==1) {
|
} else if (res.size()==1) {
|
||||||
switch(get_cmd_type(res[0])) {
|
switch(get_cmd_type(res[0])) {
|
||||||
case dbg_cmd::cmd_help: help(); break;
|
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_continue: return;
|
||||||
case dbg_cmd::cmd_list_file: list_file(); break;
|
case dbg_cmd::cmd_list_file: list_file(); break;
|
||||||
case dbg_cmd::cmd_global: gstate(); break;
|
case dbg_cmd::cmd_global: global_state(); break;
|
||||||
case dbg_cmd::cmd_local: lstate(); break;
|
case dbg_cmd::cmd_local: local_state(); break;
|
||||||
case dbg_cmd::cmd_upval: ustate(); break;
|
case dbg_cmd::cmd_upval: upvalue_state(); break;
|
||||||
case dbg_cmd::cmd_register: reginfo(); break;
|
case dbg_cmd::cmd_register: register_info(); break;
|
||||||
case dbg_cmd::cmd_show_all: detail(); break;
|
case dbg_cmd::cmd_show_all: all_state_detail(); break;
|
||||||
case dbg_cmd::cmd_next: next = true; return;
|
case dbg_cmd::cmd_next: next = true; return;
|
||||||
case dbg_cmd::cmd_exit: std::exit(0);
|
case dbg_cmd::cmd_exit: std::exit(0);
|
||||||
default: err(); break;
|
default: err(); break;
|
||||||
}
|
}
|
||||||
} else if (res.size()==3 &&
|
} else if (res.size()==3 &&
|
||||||
get_cmd_type(res[0])==dbg_cmd::cmd_break_point) {
|
get_cmd_type(res[0])==dbg_cmd::cmd_break_point) {
|
||||||
bk_fidx = file_index(res[1]);
|
break_file_index = file_index(res[1]);
|
||||||
if (bk_fidx==65535) {
|
if (break_file_index==65535) {
|
||||||
std::clog << "cannot find file named `" << res[1] << "`\n";
|
std::clog << "cannot find file named `" << res[1] << "`\n";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -223,7 +226,7 @@ void dbg::interact() {
|
||||||
if (tmp<=0) {
|
if (tmp<=0) {
|
||||||
std::clog << "incorrect line number `" << res[2] << "`\n";
|
std::clog << "incorrect line number `" << res[2] << "`\n";
|
||||||
} else {
|
} else {
|
||||||
bk_line = tmp;
|
break_line = tmp;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err();
|
err();
|
||||||
|
@ -243,9 +246,15 @@ void dbg::run(
|
||||||
|
|
||||||
const auto& file_list = linker.get_file_list();
|
const auto& file_list = linker.get_file_list();
|
||||||
fsize = file_list.size();
|
fsize = file_list.size();
|
||||||
init(gen.strs(), gen.nums(), gen.natives(),
|
init(
|
||||||
gen.codes(), gen.globals(),
|
gen.strs(),
|
||||||
file_list, argv);
|
gen.nums(),
|
||||||
|
gen.natives(),
|
||||||
|
gen.codes(),
|
||||||
|
gen.globals(),
|
||||||
|
file_list,
|
||||||
|
argv
|
||||||
|
);
|
||||||
data.init(file_list);
|
data.init(file_list);
|
||||||
|
|
||||||
std::vector<u32> code;
|
std::vector<u32> code;
|
||||||
|
|
|
@ -37,7 +37,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class dbg:public vm {
|
class dbg: public vm {
|
||||||
private:
|
private:
|
||||||
typedef void (dbg::*nasal_vm_func)();
|
typedef void (dbg::*nasal_vm_func)();
|
||||||
const nasal_vm_func operand_function[op_ret + 1] = {
|
const nasal_vm_func operand_function[op_ret + 1] = {
|
||||||
|
@ -139,8 +139,8 @@ private:
|
||||||
private:
|
private:
|
||||||
bool next;
|
bool next;
|
||||||
usize fsize;
|
usize fsize;
|
||||||
u16 bk_fidx;
|
u16 break_file_index;
|
||||||
u32 bk_line;
|
u32 break_line;
|
||||||
error src;
|
error src;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -158,8 +158,8 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
dbg():
|
dbg():
|
||||||
next(false), fsize(0),
|
next(true), fsize(0),
|
||||||
bk_fidx(0), bk_line(0),
|
break_file_index(0), break_line(0),
|
||||||
do_profiling(false) {}
|
do_profiling(false) {}
|
||||||
void run(
|
void run(
|
||||||
const codegen&,
|
const codegen&,
|
||||||
|
|
|
@ -10,11 +10,8 @@ struct for_reset {
|
||||||
for_reset() {
|
for_reset() {
|
||||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &scr);
|
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
|
#endif
|
||||||
|
|
||||||
std::ostream& back_white(std::ostream& s) {
|
std::ostream& back_white(std::ostream& s) {
|
||||||
|
@ -64,8 +61,10 @@ std::ostream& white(std::ostream& s) {
|
||||||
|
|
||||||
std::ostream& reset(std::ostream& s) {
|
std::ostream& reset(std::ostream& s) {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
|
SetConsoleTextAttribute(
|
||||||
for_reset::singleton()->scr.wAttributes);
|
GetStdHandle(STD_OUTPUT_HANDLE),
|
||||||
|
windows_system_set.scr.wAttributes
|
||||||
|
);
|
||||||
#else
|
#else
|
||||||
s << "\033[0m";
|
s << "\033[0m";
|
||||||
#endif
|
#endif
|
||||||
|
|
379
src/nasal_gc.cpp
379
src/nasal_gc.cpp
|
@ -2,348 +2,6 @@
|
||||||
|
|
||||||
namespace nasal {
|
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() {
|
void gc::do_mark_sweep() {
|
||||||
using clk = std::chrono::high_resolution_clock;
|
using clk = std::chrono::high_resolution_clock;
|
||||||
auto begin = clk::now();
|
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
|
// 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) {
|
if (i->type>vm_num) {
|
||||||
bfs_queue.push_back(*i);
|
bfs_queue.push_back(*i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bfs_queue.push_back(rctx->funcr);
|
bfs_queue.push_back(running_context->funcr);
|
||||||
bfs_queue.push_back(rctx->upvalr);
|
bfs_queue.push_back(running_context->upvalr);
|
||||||
bfs_queue.push_back(temp);
|
bfs_queue.push_back(temp);
|
||||||
|
|
||||||
if (!cort) {
|
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
|
// 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) {
|
if (i->type>vm_num) {
|
||||||
bfs_queue.push_back(*i);
|
bfs_queue.push_back(*i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bfs_queue.push_back(mctx.funcr);
|
bfs_queue.push_back(main_context.funcr);
|
||||||
bfs_queue.push_back(mctx.upvalr);
|
bfs_queue.push_back(main_context.upvalr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void gc::mark_var(std::vector<var>& bfs_queue, var& value) {
|
void gc::mark_var(std::vector<var>& bfs_queue, var& value) {
|
||||||
|
@ -556,7 +214,7 @@ void gc::init(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
strs[i] = var::gcobj(new nas_val(vm_str));
|
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];
|
strs[i].str() = constant_strings[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -568,7 +226,7 @@ void gc::init(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
env_argv[i] = var::gcobj(new nas_val(vm_str));
|
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];
|
env_argv[i].str() = argv[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -694,39 +352,34 @@ var gc::alloc(u8 type) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gc::ctxchg(nas_co& co) {
|
void gc::context_change(nas_co* co) {
|
||||||
// store running state to main context
|
// store running state to main context
|
||||||
mctx = *rctx;
|
main_context = *running_context;
|
||||||
|
|
||||||
// restore coroutine context state
|
// restore coroutine context state
|
||||||
*rctx = co.ctx;
|
*running_context = co->ctx;
|
||||||
|
|
||||||
// set coroutine pointer
|
// set coroutine pointer
|
||||||
cort = &co;
|
cort = co;
|
||||||
|
|
||||||
// set coroutine state to running
|
// set coroutine state to running
|
||||||
cort->status = nas_co::status::running;
|
cort->status = nas_co::status::running;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gc::ctxreserve() {
|
void gc::context_reserve() {
|
||||||
// pc=0 means this coroutine is finished
|
// pc=0 means this coroutine is finished
|
||||||
cort->status = rctx->pc?
|
cort->status = running_context->pc?
|
||||||
nas_co::status::suspended:
|
nas_co::status::suspended:
|
||||||
nas_co::status::dead;
|
nas_co::status::dead;
|
||||||
|
|
||||||
// store running state to coroutine
|
// store running state to coroutine
|
||||||
cort->ctx = *rctx;
|
cort->ctx = *running_context;
|
||||||
|
|
||||||
// restore main context state
|
// restore main context state
|
||||||
*rctx = mctx;
|
*running_context = main_context;
|
||||||
|
|
||||||
// set coroutine pointer to nullptr
|
// set coroutine pointer to nullptr
|
||||||
cort = 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
286
src/nasal_gc.h
286
src/nasal_gc.h
|
@ -7,293 +7,28 @@
|
||||||
#pragma warning (disable:4102)
|
#pragma warning (disable:4102)
|
||||||
#endif
|
#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 <iomanip>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <unordered_map>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <algorithm>
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include "nasal.h"
|
#include "nasal.h"
|
||||||
|
#include "nasal_type.h"
|
||||||
|
|
||||||
namespace nasal {
|
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 {
|
struct gc {
|
||||||
/* main context temporary storage */
|
/* main context temporary storage */
|
||||||
context mctx;
|
context main_context;
|
||||||
|
|
||||||
/* global storage */
|
/* global storage */
|
||||||
var* main_context_global = nullptr;
|
var* main_context_global = nullptr;
|
||||||
usize main_context_global_size = 0;
|
usize main_context_global_size = 0;
|
||||||
|
|
||||||
/* runtime context */
|
/* runtime context */
|
||||||
context* rctx = nullptr;
|
context* running_context = nullptr;
|
||||||
nas_co* cort = nullptr; // running coroutine
|
nas_co* cort = nullptr; // running coroutine
|
||||||
|
|
||||||
/* temporary space used in native/module functions */
|
/* temporary space used in native/module functions */
|
||||||
|
@ -327,7 +62,7 @@ struct gc {
|
||||||
i64 max_sweep_time = 0;
|
i64 max_sweep_time = 0;
|
||||||
|
|
||||||
void set(context* _ctx, var* _global, usize _size) {
|
void set(context* _ctx, var* _global, usize _size) {
|
||||||
rctx = _ctx;
|
running_context = _ctx;
|
||||||
main_context_global = _global;
|
main_context_global = _global;
|
||||||
main_context_global_size = _size;
|
main_context_global_size = _size;
|
||||||
}
|
}
|
||||||
|
@ -353,8 +88,8 @@ public:
|
||||||
void clear();
|
void clear();
|
||||||
void info() const;
|
void info() const;
|
||||||
var alloc(const u8);
|
var alloc(const u8);
|
||||||
void ctxchg(nas_co&);
|
void context_change(nas_co*);
|
||||||
void ctxreserve();
|
void context_reserve();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
var newstr(char c) {
|
var newstr(char c) {
|
||||||
|
@ -364,21 +99,18 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
var newstr(const char* buff) {
|
var newstr(const char* buff) {
|
||||||
var s=alloc(vm_str);
|
var s = alloc(vm_str);
|
||||||
s.str() = buff;
|
s.str() = std::string(buff);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
var newstr(const std::string& buff) {
|
var newstr(const std::string& buff) {
|
||||||
var s=alloc(vm_str);
|
var s = alloc(vm_str);
|
||||||
s.str() = buff;
|
s.str() = buff;
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// use to print error log and return error value
|
|
||||||
var nas_err(const std::string&, const std::string&);
|
|
||||||
|
|
||||||
// module function type
|
// module function type
|
||||||
typedef var (*module_func)(var*, usize, gc*);
|
typedef var (*module_func)(var*, usize, gc*);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include "nasal_import.h"
|
#include "nasal_import.h"
|
||||||
#include "symbol_finder.h"
|
#include "symbol_finder.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
||||||
linker::linker():
|
linker::linker():
|
||||||
|
@ -196,8 +198,10 @@ code_block* linker::import_regular_file(call_expr* node) {
|
||||||
return new code_block({0, 0, 0, 0, filename});
|
return new code_block({0, 0, 0, 0, filename});
|
||||||
}
|
}
|
||||||
if (check_self_import(filename)) {
|
if (check_self_import(filename)) {
|
||||||
err.err("link", "self-referenced module <" + filename + ">:\n" +
|
err.err("link",
|
||||||
" reference path: " + generate_self_import_path(filename));
|
"self-referenced module <" + filename + ">:\n" +
|
||||||
|
" reference path: " + generate_self_import_path(filename)
|
||||||
|
);
|
||||||
return new code_block({0, 0, 0, 0, filename});
|
return new code_block({0, 0, 0, 0, filename});
|
||||||
}
|
}
|
||||||
exist(filename);
|
exist(filename);
|
||||||
|
@ -206,16 +210,19 @@ code_block* linker::import_regular_file(call_expr* node) {
|
||||||
// start importing...
|
// start importing...
|
||||||
if (lex.scan(filename).geterr()) {
|
if (lex.scan(filename).geterr()) {
|
||||||
err.err("link", "error occurred when analysing <" + filename + ">");
|
err.err("link", "error occurred when analysing <" + filename + ">");
|
||||||
|
return new code_block({0, 0, 0, 0, filename});
|
||||||
}
|
}
|
||||||
if (par.compile(lex).geterr()) {
|
if (par.compile(lex).geterr()) {
|
||||||
err.err("link", "error occurred when analysing <" + filename + ">");
|
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 parse_result = par.swap(nullptr);
|
||||||
auto res = load(tmp, find(filename));
|
|
||||||
|
// check if parse result has 'import'
|
||||||
|
auto result = load(parse_result, find(filename));
|
||||||
module_load_stack.pop_back();
|
module_load_stack.pop_back();
|
||||||
return res;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
code_block* linker::import_nasal_lib() {
|
code_block* linker::import_nasal_lib() {
|
||||||
|
@ -234,52 +241,88 @@ code_block* linker::import_nasal_lib() {
|
||||||
|
|
||||||
// start importing...
|
// start importing...
|
||||||
if (lex.scan(filename).geterr()) {
|
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()) {
|
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'
|
auto parse_result = par.swap(nullptr);
|
||||||
return load(tmp, find(filename));
|
// 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) {
|
std::string linker::generate_module_name(const std::string& file_path) {
|
||||||
auto error_name = "error_generated@[" + filename + "]";
|
auto error_name = "module@[" + file_path + "]";
|
||||||
auto pos = filename.find_last_of(".nas");
|
if (!file_path.length()) {
|
||||||
if (pos==std::string::npos) {
|
|
||||||
return error_name;
|
return error_name;
|
||||||
}
|
}
|
||||||
pos -= 4;
|
|
||||||
auto split_pos = filename.find_last_of("/");
|
// check file suffix and get file suffix position
|
||||||
if (split_pos==std::string::npos) {
|
auto suffix_position = file_path.find(".nas");
|
||||||
split_pos = filename.find_last_of("\\");
|
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?
|
if (suffix_position+4!=file_path.length()) {
|
||||||
filename.substr(0, pos + 1):
|
err.warn("link",
|
||||||
filename.substr(split_pos + 1, pos - split_pos);
|
"get invalid module name from <" + file_path + ">, " +
|
||||||
if (!res.length()) {
|
"will not be easily accessed. " +
|
||||||
err.warn("link", "get empty module name from <" + filename + ">, " +
|
"only one \".nas\" suffix is required in the path."
|
||||||
"will not be easily accessed.");
|
);
|
||||||
|
return error_name;
|
||||||
}
|
}
|
||||||
if (res.length() && '0' <= res[0] && res[0] <= '9') {
|
|
||||||
err.warn("link", "get module <" + res + "> from <" + filename + ">, " +
|
// only get the file name as module name, directory path is not included
|
||||||
"will not be easily accessed.");
|
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 + ">, " +
|
// split file path to get module name
|
||||||
"will not be easily accessed.");
|
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) {
|
return_expr* linker::generate_module_return(code_block* block) {
|
||||||
auto sf = new symbol_finder;
|
auto finder = std::unique_ptr<symbol_finder>(new symbol_finder);
|
||||||
auto res = new return_expr(block->get_location());
|
auto result = new return_expr(block->get_location());
|
||||||
auto value = new hash_expr(block->get_location());
|
auto value = new hash_expr(block->get_location());
|
||||||
res->set_value(value);
|
result->set_value(value);
|
||||||
for(const auto& i : sf->do_find(block)) {
|
for(const auto& i : finder->do_find(block)) {
|
||||||
auto pair = new hash_pair(block->get_location());
|
auto pair = new hash_pair(block->get_location());
|
||||||
// do not export symbol begins with '_'
|
// do not export symbol begins with '_'
|
||||||
if (i.name.length() && i.name[0]=='_') {
|
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));
|
pair->set_value(new identifier(block->get_location(), i.name));
|
||||||
value->add_member(pair);
|
value->add_member(pair);
|
||||||
}
|
}
|
||||||
delete sf;
|
return result;
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
definition_expr* linker::generate_module_definition(code_block* block) {
|
definition_expr* linker::generate_module_definition(code_block* block) {
|
||||||
|
|
|
@ -120,7 +120,8 @@ f64 oct2f(const char* str) {
|
||||||
// but this also makes 0.1+0.2==0.3,
|
// but this also makes 0.1+0.2==0.3,
|
||||||
// not another result that you may get in other languages.
|
// not another result that you may get in other languages.
|
||||||
f64 dec2f(const char* str) {
|
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') {
|
while('0'<=*str && *str<='9') {
|
||||||
ret = ret*10+(*str++-'0');
|
ret = ret*10+(*str++-'0');
|
||||||
}
|
}
|
||||||
|
@ -147,7 +148,7 @@ f64 dec2f(const char* str) {
|
||||||
return nan("");
|
return nan("");
|
||||||
}
|
}
|
||||||
if (*str=='-' || *str=='+') {
|
if (*str=='-' || *str=='+') {
|
||||||
negative = (*str++=='-'? -1:1);
|
negative = (*str++=='-');
|
||||||
}
|
}
|
||||||
if (!*str) {
|
if (!*str) {
|
||||||
return nan("");
|
return nan("");
|
||||||
|
@ -159,7 +160,9 @@ f64 dec2f(const char* str) {
|
||||||
if (*str) {
|
if (*str) {
|
||||||
return nan("");
|
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) {
|
f64 str2num(const char* str) {
|
||||||
|
|
|
@ -28,13 +28,13 @@ const char* opname[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
void codestream::set(
|
void codestream::set(
|
||||||
const f64* num_buff,
|
const f64* number_list,
|
||||||
const std::string* str_buff,
|
const std::string* string_list,
|
||||||
const nasal_builtin_table* native_table_ptr,
|
const nasal_builtin_table* native_table,
|
||||||
const std::string* file_list) {
|
const std::string* file_list) {
|
||||||
nums = num_buff;
|
const_number = number_list;
|
||||||
strs = str_buff;
|
const_string = string_list;
|
||||||
natives = native_table_ptr;
|
natives = native_table;
|
||||||
files = file_list;
|
files = file_list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,24 +62,24 @@ void codestream::dump(std::ostream& out) const {
|
||||||
case op_addeqc: case op_subeqc:
|
case op_addeqc: case op_subeqc:
|
||||||
case op_muleqc:case op_diveqc:
|
case op_muleqc:case op_diveqc:
|
||||||
out << hex << "0x" << num << dec
|
out << hex << "0x" << num << dec
|
||||||
<< " (" << nums[num] << ")"; break;
|
<< " (" << const_number[num] << ")"; break;
|
||||||
case op_lnkeqc:
|
case op_lnkeqc:
|
||||||
out << hex << "0x" << num << dec
|
out << hex << "0x" << num << dec
|
||||||
<< " (" << rawstr(strs[num], 16) << ")"; break;
|
<< " (" << rawstr(const_string[num], 16) << ")"; break;
|
||||||
case op_addecp: case op_subecp:
|
case op_addecp: case op_subecp:
|
||||||
case op_mulecp: case op_divecp:
|
case op_mulecp: case op_divecp:
|
||||||
out << hex << "0x" << num << dec
|
out << hex << "0x" << num << dec
|
||||||
<< " (" << nums[num] << ") sp-1"; break;
|
<< " (" << const_number[num] << ") sp-1"; break;
|
||||||
case op_lnkecp:
|
case op_lnkecp:
|
||||||
out << hex << "0x" << num << dec
|
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_addc: case op_subc:
|
||||||
case op_mulc: case op_divc:
|
case op_mulc: case op_divc:
|
||||||
case op_lessc: case op_leqc:
|
case op_lessc: case op_leqc:
|
||||||
case op_grtc: case op_geqc:
|
case op_grtc: case op_geqc:
|
||||||
case op_pnum:
|
case op_pnum:
|
||||||
out << hex << "0x" << num << dec
|
out << hex << "0x" << num << dec
|
||||||
<< " (" << nums[num] << ")"; break;
|
<< " (" << const_number[num] << ")"; break;
|
||||||
case op_callvi: case op_newv:
|
case op_callvi: case op_newv:
|
||||||
case op_callfv: case op_repl:
|
case op_callfv: case op_repl:
|
||||||
case op_intl: case op_findex:
|
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_mcallh: case op_para:
|
||||||
case op_deft: case op_dyn:
|
case op_deft: case op_dyn:
|
||||||
out << hex << "0x" << num << dec
|
out << hex << "0x" << num << dec
|
||||||
<< " (" << rawstr(strs[num], 16) << ")"; break;
|
<< " (" << rawstr(const_string[num], 16) << ")"; break;
|
||||||
default:
|
default:
|
||||||
if (files) {
|
if (files) {
|
||||||
out << hex << "0x" << num << dec;
|
out << hex << "0x" << num << dec;
|
||||||
|
|
|
@ -111,8 +111,8 @@ class codestream {
|
||||||
private:
|
private:
|
||||||
opcode code;
|
opcode code;
|
||||||
const u32 index;
|
const u32 index;
|
||||||
inline static const f64* nums = nullptr;
|
inline static const f64* const_number = nullptr;
|
||||||
inline static const std::string* strs = nullptr;
|
inline static const std::string* const_string = nullptr;
|
||||||
inline static const nasal_builtin_table* natives = nullptr;
|
inline static const nasal_builtin_table* natives = nullptr;
|
||||||
inline static const std::string* files = nullptr;
|
inline static const std::string* files = nullptr;
|
||||||
|
|
||||||
|
|
|
@ -971,20 +971,26 @@ forei_expr* parse::forei_loop() {
|
||||||
|
|
||||||
iter_expr* parse::iter_gen() {
|
iter_expr* parse::iter_gen() {
|
||||||
auto node = new iter_expr(toks[ptr].loc);
|
auto node = new iter_expr(toks[ptr].loc);
|
||||||
|
// definition
|
||||||
if (lookahead(tok::var)) {
|
if (lookahead(tok::var)) {
|
||||||
match(tok::var);
|
match(tok::var);
|
||||||
node->set_name(id());
|
node->set_name(id());
|
||||||
|
node->set_is_definition(true);
|
||||||
update_location(node);
|
update_location(node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// single symbol call
|
||||||
auto id_node = id();
|
auto id_node = id();
|
||||||
if (!is_call(toks[ptr].type)) {
|
if (!is_call(toks[ptr].type)) {
|
||||||
node->set_name(id_node);
|
node->set_name(id_node);
|
||||||
update_location(node);
|
update_location(node);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// call expression
|
||||||
auto tmp = new call_expr(id_node->get_location());
|
auto tmp = new call_expr(id_node->get_location());
|
||||||
tmp->set_first(id());
|
tmp->set_first(id_node);
|
||||||
while(is_call(toks[ptr].type)) {
|
while(is_call(toks[ptr].type)) {
|
||||||
tmp->add_call(call_scalar());
|
tmp->add_call(call_scalar());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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&);
|
||||||
|
|
||||||
|
}
|
235
src/nasal_vm.cpp
235
src/nasal_vm.cpp
|
@ -11,14 +11,14 @@ void vm::init(
|
||||||
const std::vector<std::string>& filenames,
|
const std::vector<std::string>& filenames,
|
||||||
const std::vector<std::string>& argv
|
const std::vector<std::string>& argv
|
||||||
) {
|
) {
|
||||||
cnum = nums.data();
|
const_number = nums.data();
|
||||||
cstr = strs.data();
|
const_string = strs.data();
|
||||||
bytecode = code.data();
|
bytecode = code.data();
|
||||||
files = filenames.data();
|
files = filenames.data();
|
||||||
global_size = global_symbol.size();
|
global_size = global_symbol.size();
|
||||||
|
|
||||||
/* set native functions */
|
/* set native functions */
|
||||||
native = natives;
|
native_function = natives;
|
||||||
|
|
||||||
/* set context and global */
|
/* set context and global */
|
||||||
if (!is_repl_mode || first_exec_flag) {
|
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);
|
const auto p = reinterpret_cast<u64>(val.val.gcobj);
|
||||||
switch(val.type) {
|
switch(val.type) {
|
||||||
case vm_none: std::clog << "| null |"; break;
|
case vm_none: std::clog << "| null |"; break;
|
||||||
|
@ -93,7 +93,7 @@ void vm::valinfo(var& val) {
|
||||||
<< " val}"; break;
|
<< " val}"; break;
|
||||||
case vm_obj: std::clog << "| obj | <0x" << std::hex << p
|
case vm_obj: std::clog << "| obj | <0x" << std::hex << p
|
||||||
<< "> obj:0x"
|
<< "> obj:0x"
|
||||||
<< reinterpret_cast<u64>(val.obj().ptr)
|
<< reinterpret_cast<u64>(val.ghost().pointer)
|
||||||
<< std::dec; break;
|
<< std::dec; break;
|
||||||
case vm_co: std::clog << "| co | <0x" << std::hex << p
|
case vm_co: std::clog << "| co | <0x" << std::hex << p
|
||||||
<< std::dec << "> coroutine"; break;
|
<< std::dec << "> coroutine"; break;
|
||||||
|
@ -106,22 +106,84 @@ void vm::valinfo(var& val) {
|
||||||
std::clog << "\n";
|
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* bottom = ctx.stack;
|
||||||
var* top = ctx.top;
|
var* top = ctx.top;
|
||||||
|
|
||||||
std::stack<u32> ret;
|
// generate trace back
|
||||||
|
std::stack<const nas_func*> functions;
|
||||||
for(var* i = bottom; i<=top; ++i) {
|
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) {
|
if (i->type==vm_ret && i->ret()!=0) {
|
||||||
ret.push(i->ret());
|
ret.push(i->ret());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret.push(ctx.pc); // store the position program crashed
|
ret.push(ctx.pc); // store the position program crashed
|
||||||
|
|
||||||
std::clog << "trace back ("
|
std::clog << "\ntrace back " << (ngc.cort? "(coroutine)":"(main)") << "\n";
|
||||||
<< (ngc.cort? "coroutine":"main")
|
codestream::set(const_number, const_string, native_function.data(), files);
|
||||||
<< ")\n";
|
|
||||||
codestream::set(cnum, cstr, native.data(), files);
|
|
||||||
for(u32 p = 0, same = 0, prev = 0xffffffff; !ret.empty(); prev = p, ret.pop()) {
|
for(u32 p = 0, same = 0, prev = 0xffffffff; !ret.empty(); prev = p, ret.pop()) {
|
||||||
if ((p = ret.top())==prev) {
|
if ((p = ret.top())==prev) {
|
||||||
++same;
|
++same;
|
||||||
|
@ -132,17 +194,17 @@ void vm::traceback() {
|
||||||
<< std::setw(6) << std::setfill('0')
|
<< std::setw(6) << std::setfill('0')
|
||||||
<< prev << std::dec << " "
|
<< prev << std::dec << " "
|
||||||
<< same << " same call(s)\n";
|
<< same << " same call(s)\n";
|
||||||
|
same = 0;
|
||||||
}
|
}
|
||||||
same = 0;
|
|
||||||
std::clog << " " << codestream(bytecode[p], p) << "\n";
|
std::clog << " " << codestream(bytecode[p], p) << "\n";
|
||||||
}
|
}
|
||||||
// the first called place has no same calls
|
// 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* top = ctx.top;
|
||||||
var* bottom = ctx.stack;
|
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 << std::dec << ", limit " << limit << ", total ";
|
||||||
std::clog << (top<bottom? 0:static_cast<i64>(top-bottom+1)) << ")\n";
|
std::clog << (top<bottom? 0:static_cast<i64>(top-bottom+1)) << ")\n";
|
||||||
for(u32 i = 0; i<limit && top>=bottom; ++i, --top) {
|
for(u32 i = 0; i<limit && top>=bottom; ++i, --top) {
|
||||||
|
@ -150,12 +212,12 @@ void vm::stackinfo(const u32 limit = 10) {
|
||||||
<< std::setw(6) << std::setfill('0')
|
<< std::setw(6) << std::setfill('0')
|
||||||
<< static_cast<u64>(top-bottom) << std::dec
|
<< static_cast<u64>(top-bottom) << std::dec
|
||||||
<< " ";
|
<< " ";
|
||||||
valinfo(top[0]);
|
value_info(top[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void vm::reginfo() {
|
void vm::register_info() {
|
||||||
std::clog << "registers (" << (ngc.cort? "coroutine":"main")
|
std::clog << "\nregisters (" << (ngc.cort? "coroutine":"main")
|
||||||
<< ")\n" << std::hex
|
<< ")\n" << std::hex
|
||||||
<< " [pc ] | pc | 0x" << ctx.pc << "\n"
|
<< " [pc ] | pc | 0x" << ctx.pc << "\n"
|
||||||
<< " [global] | addr | 0x"
|
<< " [global] | addr | 0x"
|
||||||
|
@ -169,45 +231,45 @@ void vm::reginfo() {
|
||||||
<< " [top ] | addr | 0x"
|
<< " [top ] | addr | 0x"
|
||||||
<< reinterpret_cast<u64>(ctx.top) << "\n"
|
<< reinterpret_cast<u64>(ctx.top) << "\n"
|
||||||
<< std::dec;
|
<< std::dec;
|
||||||
std::clog << " [funcr ] "; valinfo(ctx.funcr);
|
std::clog << " [funcr ] "; value_info(ctx.funcr);
|
||||||
std::clog << " [upval ] "; valinfo(ctx.upvalr);
|
std::clog << " [upval ] "; value_info(ctx.upvalr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vm::gstate() {
|
void vm::global_state() {
|
||||||
if (!global_size || global[0].type==vm_none) {
|
if (!global_size || global[0].type==vm_none) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::clog << "global (0x" << std::hex
|
std::clog << "\nglobal (0x" << std::hex
|
||||||
<< reinterpret_cast<u64>(global) << ")\n" << std::dec;
|
<< reinterpret_cast<u64>(global) << ")\n" << std::dec;
|
||||||
for(usize i = 0; i<global_size; ++i) {
|
for(usize i = 0; i<global_size; ++i) {
|
||||||
std::clog << " 0x" << std::hex << std::setw(6)
|
std::clog << " 0x" << std::hex << std::setw(6)
|
||||||
<< std::setfill('0') << i << std::dec
|
<< std::setfill('0') << i << std::dec
|
||||||
<< " ";
|
<< " ";
|
||||||
valinfo(global[i]);
|
value_info(global[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void vm::lstate() {
|
void vm::local_state() {
|
||||||
if (!ctx.localr || !ctx.funcr.func().lsize) {
|
if (!ctx.localr || !ctx.funcr.func().local_size) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const u32 lsize = ctx.funcr.func().lsize;
|
const u32 lsize = ctx.funcr.func().local_size;
|
||||||
std::clog << "local (0x" << std::hex << reinterpret_cast<u64>(ctx.localr)
|
std::clog << "\nlocal (0x" << std::hex << reinterpret_cast<u64>(ctx.localr)
|
||||||
<< " <+" << static_cast<u64>(ctx.localr-ctx.stack)
|
<< " <+" << static_cast<u64>(ctx.localr-ctx.stack)
|
||||||
<< ">)\n" << std::dec;
|
<< ">)\n" << std::dec;
|
||||||
for(u32 i = 0; i<lsize; ++i) {
|
for(u32 i = 0; i<lsize; ++i) {
|
||||||
std::clog << " 0x" << std::hex << std::setw(6)
|
std::clog << " 0x" << std::hex << std::setw(6)
|
||||||
<< std::setfill('0') << i << std::dec
|
<< 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()) {
|
if (ctx.funcr.type==vm_nil || ctx.funcr.func().upval.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::clog << "upvalue\n";
|
std::clog << "\nupvalue\n";
|
||||||
auto& upval = ctx.funcr.func().upval;
|
auto& upval = ctx.funcr.func().upval;
|
||||||
for(u32 i = 0; i<upval.size(); ++i) {
|
for(u32 i = 0; i<upval.size(); ++i) {
|
||||||
std::clog << " -> upval[" << i << "]:\n";
|
std::clog << " -> upval[" << i << "]:\n";
|
||||||
|
@ -216,26 +278,121 @@ void vm::ustate() {
|
||||||
std::clog << " 0x" << std::hex << std::setw(6)
|
std::clog << " 0x" << std::hex << std::setw(6)
|
||||||
<< std::setfill('0') << j << std::dec
|
<< std::setfill('0') << j << std::dec
|
||||||
<< " ";
|
<< " ";
|
||||||
valinfo(uv[j]);
|
value_info(uv[j]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void vm::detail() {
|
void vm::all_state_detail() {
|
||||||
reginfo();
|
register_info();
|
||||||
gstate();
|
global_state();
|
||||||
lstate();
|
local_state();
|
||||||
ustate();
|
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) {
|
void vm::die(const std::string& str) {
|
||||||
std::cerr << "[vm] error: " << str << "\n";
|
std::cerr << "[vm] error: " << str << "\n";
|
||||||
traceback();
|
function_call_trace();
|
||||||
stackinfo();
|
trace_back();
|
||||||
|
stack_info();
|
||||||
|
|
||||||
// show verbose crash info
|
// show verbose crash info
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
detail();
|
all_state_detail();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ngc.cort) {
|
if (!ngc.cort) {
|
||||||
|
@ -244,7 +401,7 @@ void vm::die(const std::string& str) {
|
||||||
} else {
|
} else {
|
||||||
// in coroutine, shut down the coroutine and return to main context
|
// in coroutine, shut down the coroutine and return to main context
|
||||||
ctx.pc = 0; // mark coroutine 'dead'
|
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'
|
ctx.top[0] = nil; // generate return value 'nil'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
298
src/nasal_vm.h
298
src/nasal_vm.h
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
#include <cstring>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "nasal_import.h"
|
#include "nasal_import.h"
|
||||||
#include "nasal_gc.h"
|
#include "nasal_gc.h"
|
||||||
|
@ -22,10 +24,10 @@ protected:
|
||||||
context ctx;
|
context ctx;
|
||||||
|
|
||||||
/* constants */
|
/* constants */
|
||||||
const f64* cnum = nullptr; // constant numbers
|
const f64* const_number = nullptr; // constant numbers
|
||||||
const std::string* cstr = nullptr; // constant symbols and strings
|
const std::string* const_string = nullptr; // constant symbols and strings
|
||||||
std::vector<u32> imm; // immediate number table
|
std::vector<u32> imm; // immediate number table
|
||||||
std::vector<nasal_builtin_table> native;
|
std::vector<nasal_builtin_table> native_function;
|
||||||
|
|
||||||
/* garbage collector */
|
/* garbage collector */
|
||||||
gc ngc;
|
gc ngc;
|
||||||
|
@ -57,14 +59,21 @@ protected:
|
||||||
|
|
||||||
/* debug functions */
|
/* debug functions */
|
||||||
bool verbose = false;
|
bool verbose = false;
|
||||||
void valinfo(var&);
|
void value_info(var&);
|
||||||
void traceback();
|
void function_detail_info(const nas_func&);
|
||||||
void stackinfo(const u32);
|
void function_call_trace();
|
||||||
void reginfo();
|
void trace_back();
|
||||||
void gstate();
|
void stack_info(const u32);
|
||||||
void lstate();
|
void register_info();
|
||||||
void ustate();
|
void global_state();
|
||||||
void detail();
|
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&);
|
void die(const std::string&);
|
||||||
|
|
||||||
/* vm calculation functions*/
|
/* vm calculation functions*/
|
||||||
|
@ -204,7 +213,7 @@ inline void vm::o_repl() {
|
||||||
|
|
||||||
inline void vm::o_intl() {
|
inline void vm::o_intl() {
|
||||||
ctx.top[0].func().local.resize(imm[ctx.pc], nil);
|
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() {
|
inline void vm::o_loadg() {
|
||||||
|
@ -217,11 +226,11 @@ inline void vm::o_loadl() {
|
||||||
|
|
||||||
inline void vm::o_loadu() {
|
inline void vm::o_loadu() {
|
||||||
ctx.funcr.func().upval[(imm[ctx.pc]>>16)&0xffff]
|
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() {
|
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() {
|
inline void vm::o_pnil() {
|
||||||
|
@ -252,7 +261,7 @@ inline void vm::o_newf() {
|
||||||
(++ctx.top)[0] = ngc.alloc(vm_func);
|
(++ctx.top)[0] = ngc.alloc(vm_func);
|
||||||
auto& func = ctx.top[0].func();
|
auto& func = ctx.top[0].func();
|
||||||
func.entry = imm[ctx.pc];
|
func.entry = imm[ctx.pc];
|
||||||
func.psize = 1;
|
func.parameter_size = 1;
|
||||||
|
|
||||||
/* this means you create a new function in local scope */
|
/* this means you create a new function in local scope */
|
||||||
if (ctx.localr) {
|
if (ctx.localr) {
|
||||||
|
@ -260,35 +269,35 @@ inline void vm::o_newf() {
|
||||||
// function created in the same local scope shares one closure
|
// function created in the same local scope shares one closure
|
||||||
// so this size & stk setting has no problem
|
// so this size & stk setting has no problem
|
||||||
var upval = (ctx.upvalr.type==vm_nil)? ngc.alloc(vm_upval):ctx.upvalr;
|
var upval = (ctx.upvalr.type==vm_nil)? ngc.alloc(vm_upval):ctx.upvalr;
|
||||||
upval.upval().size = ctx.funcr.func().lsize;
|
upval.upval().size = ctx.funcr.func().local_size;
|
||||||
upval.upval().stk = ctx.localr;
|
upval.upval().stack_frame_offset = ctx.localr;
|
||||||
func.upval.push_back(upval);
|
func.upval.push_back(upval);
|
||||||
ctx.upvalr = upval;
|
ctx.upvalr = upval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void vm::o_happ() {
|
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;
|
--ctx.top;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void vm::o_para() {
|
inline void vm::o_para() {
|
||||||
auto& func = ctx.top[0].func();
|
auto& func = ctx.top[0].func();
|
||||||
// func->size has 1 place reserved for "me"
|
// func->size has 1 place reserved for "me"
|
||||||
func.keys[imm[ctx.pc]] = func.psize;
|
func.keys[const_string[imm[ctx.pc]]] = func.parameter_size;
|
||||||
func.local[func.psize++] = var::none();
|
func.local[func.parameter_size++] = var::none();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void vm::o_deft() {
|
inline void vm::o_deft() {
|
||||||
var val = ctx.top[0];
|
var val = ctx.top[0];
|
||||||
auto& func = (--ctx.top)[0].func();
|
auto& func = (--ctx.top)[0].func();
|
||||||
// func->size has 1 place reserved for "me"
|
// func->size has 1 place reserved for "me"
|
||||||
func.keys[imm[ctx.pc]] = func.psize;
|
func.keys[const_string[imm[ctx.pc]]] = func.parameter_size;
|
||||||
func.local[func.psize++] = val;
|
func.local[func.parameter_size++] = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void vm::o_dyn() {
|
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() {
|
inline void vm::o_lnot() {
|
||||||
|
@ -304,12 +313,14 @@ inline void vm::o_lnot() {
|
||||||
ctx.top[0] = num? zero:one;
|
ctx.top[0] = num? zero:one;
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
default: die("incorrect value type"); return;
|
default:
|
||||||
|
die("cannot do not-operation on "+type_name_string(val));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void vm::o_usub() {
|
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() {
|
inline void vm::o_bnot() {
|
||||||
|
@ -318,30 +329,30 @@ inline void vm::o_bnot() {
|
||||||
|
|
||||||
inline void vm::o_btor() {
|
inline void vm::o_btor() {
|
||||||
ctx.top[-1] = var::num(
|
ctx.top[-1] = var::num(
|
||||||
static_cast<i32>(ctx.top[-1].tonum())|
|
static_cast<i32>(ctx.top[-1].to_num())|
|
||||||
static_cast<i32>(ctx.top[0].tonum())
|
static_cast<i32>(ctx.top[0].to_num())
|
||||||
);
|
);
|
||||||
--ctx.top;
|
--ctx.top;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void vm::o_btxor() {
|
inline void vm::o_btxor() {
|
||||||
ctx.top[-1] = var::num(
|
ctx.top[-1] = var::num(
|
||||||
static_cast<i32>(ctx.top[-1].tonum())^
|
static_cast<i32>(ctx.top[-1].to_num())^
|
||||||
static_cast<i32>(ctx.top[0].tonum())
|
static_cast<i32>(ctx.top[0].to_num())
|
||||||
);
|
);
|
||||||
--ctx.top;
|
--ctx.top;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void vm::o_btand() {
|
inline void vm::o_btand() {
|
||||||
ctx.top[-1] = var::num(
|
ctx.top[-1] = var::num(
|
||||||
static_cast<i32>(ctx.top[-1].tonum())&
|
static_cast<i32>(ctx.top[-1].to_num())&
|
||||||
static_cast<i32>(ctx.top[0].tonum())
|
static_cast<i32>(ctx.top[0].to_num())
|
||||||
);
|
);
|
||||||
--ctx.top;
|
--ctx.top;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define op_calc(type)\
|
#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;
|
--ctx.top;
|
||||||
|
|
||||||
inline void vm::o_add() {op_calc(+);}
|
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_mul() {op_calc(*);}
|
||||||
inline void vm::o_div() {op_calc(/);}
|
inline void vm::o_div() {op_calc(/);}
|
||||||
inline void vm::o_lnk() {
|
inline void vm::o_lnk() {
|
||||||
|
// concat two vectors into one
|
||||||
if (ctx.top[-1].type==vm_vec && ctx.top[0].type==vm_vec) {
|
if (ctx.top[-1].type==vm_vec && ctx.top[0].type==vm_vec) {
|
||||||
ngc.temp = ngc.alloc(vm_vec);
|
ngc.temp = ngc.alloc(vm_vec);
|
||||||
for(auto i : ctx.top[-1].vec().elems) {
|
for(auto i : ctx.top[-1].vec().elems) {
|
||||||
|
@ -362,19 +374,20 @@ inline void vm::o_lnk() {
|
||||||
--ctx.top;
|
--ctx.top;
|
||||||
return;
|
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;
|
--ctx.top;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define op_calc_const(type)\
|
#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_addc() {op_calc_const(+);}
|
||||||
inline void vm::o_subc() {op_calc_const(-);}
|
inline void vm::o_subc() {op_calc_const(-);}
|
||||||
inline void vm::o_mulc() {op_calc_const(*);}
|
inline void vm::o_mulc() {op_calc_const(*);}
|
||||||
inline void vm::o_divc() {op_calc_const(/);}
|
inline void vm::o_divc() {op_calc_const(/);}
|
||||||
inline void vm::o_lnkc() {
|
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
|
// 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
|
// 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
|
// but if b+=a+=c; the result of 'a+c' will be used later, imm[pc] = 0
|
||||||
#define op_calc_eq(type)\
|
#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.memr = nullptr;\
|
||||||
ctx.top -= imm[ctx.pc]+1;
|
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_diveq() {op_calc_eq(/);}
|
||||||
inline void vm::o_lnkeq() {
|
inline void vm::o_lnkeq() {
|
||||||
ctx.top[-1] = ctx.memr[0] = ngc.newstr(
|
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.memr = nullptr;
|
||||||
ctx.top -= imm[ctx.pc]+1;
|
ctx.top -= imm[ctx.pc]+1;
|
||||||
|
@ -401,8 +416,8 @@ inline void vm::o_lnkeq() {
|
||||||
|
|
||||||
inline void vm::o_bandeq() {
|
inline void vm::o_bandeq() {
|
||||||
ctx.top[-1] = ctx.memr[0] = var::num(
|
ctx.top[-1] = ctx.memr[0] = var::num(
|
||||||
static_cast<i32>(ctx.memr[0].tonum())&
|
static_cast<i32>(ctx.memr[0].to_num())&
|
||||||
static_cast<i32>(ctx.top[-1].tonum())
|
static_cast<i32>(ctx.top[-1].to_num())
|
||||||
);
|
);
|
||||||
ctx.memr = nullptr;
|
ctx.memr = nullptr;
|
||||||
ctx.top -= imm[ctx.pc]+1;
|
ctx.top -= imm[ctx.pc]+1;
|
||||||
|
@ -410,8 +425,8 @@ inline void vm::o_bandeq() {
|
||||||
|
|
||||||
inline void vm::o_boreq() {
|
inline void vm::o_boreq() {
|
||||||
ctx.top[-1] = ctx.memr[0] = var::num(
|
ctx.top[-1] = ctx.memr[0] = var::num(
|
||||||
static_cast<i32>(ctx.memr[0].tonum())|
|
static_cast<i32>(ctx.memr[0].to_num())|
|
||||||
static_cast<i32>(ctx.top[-1].tonum())
|
static_cast<i32>(ctx.top[-1].to_num())
|
||||||
);
|
);
|
||||||
ctx.memr = nullptr;
|
ctx.memr = nullptr;
|
||||||
ctx.top -= imm[ctx.pc]+1;
|
ctx.top -= imm[ctx.pc]+1;
|
||||||
|
@ -419,8 +434,8 @@ inline void vm::o_boreq() {
|
||||||
|
|
||||||
inline void vm::o_bxoreq() {
|
inline void vm::o_bxoreq() {
|
||||||
ctx.top[-1] = ctx.memr[0] = var::num(
|
ctx.top[-1] = ctx.memr[0] = var::num(
|
||||||
static_cast<i32>(ctx.memr[0].tonum())^
|
static_cast<i32>(ctx.memr[0].to_num())^
|
||||||
static_cast<i32>(ctx.top[-1].tonum())
|
static_cast<i32>(ctx.top[-1].to_num())
|
||||||
);
|
);
|
||||||
ctx.memr = nullptr;
|
ctx.memr = nullptr;
|
||||||
ctx.top -= imm[ctx.pc]+1;
|
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
|
// 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
|
// but if b+=a+=1; the result of 'a+1' will be used later, imm[pc]>>31=0
|
||||||
#define op_calc_eq_const(type)\
|
#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;
|
ctx.memr = nullptr;
|
||||||
|
|
||||||
inline void vm::o_addeqc() {op_calc_eq_const(+);}
|
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_muleqc() {op_calc_eq_const(*);}
|
||||||
inline void vm::o_diveqc() {op_calc_eq_const(/);}
|
inline void vm::o_diveqc() {op_calc_eq_const(/);}
|
||||||
inline void vm::o_lnkeqc() {
|
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;
|
ctx.memr = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define op_calc_eq_const_and_pop(type)\
|
#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.memr = nullptr;\
|
||||||
--ctx.top;
|
--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_divecp() {op_calc_eq_const_and_pop(/);}
|
||||||
inline void vm::o_lnkecp() {
|
inline void vm::o_lnkecp() {
|
||||||
ctx.top[0] = ctx.memr[0] = ngc.newstr(
|
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.memr = nullptr;
|
||||||
--ctx.top;
|
--ctx.top;
|
||||||
|
@ -481,7 +502,7 @@ inline void vm::o_eq() {
|
||||||
ctx.top[0] = (val1.str()==val2.str())? one:zero;
|
ctx.top[0] = (val1.str()==val2.str())? one:zero;
|
||||||
} else if ((val1.type==vm_num || val2.type==vm_num)
|
} else if ((val1.type==vm_num || val2.type==vm_num)
|
||||||
&& val1.type!=vm_nil && val2.type!=vm_nil) {
|
&& 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 {
|
} else {
|
||||||
ctx.top[0] = (val1==val2)? one:zero;
|
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;
|
ctx.top[0] = (val1.str()!=val2.str())? one:zero;
|
||||||
} else if ((val1.type==vm_num || val2.type==vm_num)
|
} else if ((val1.type==vm_num || val2.type==vm_num)
|
||||||
&& val1.type!=vm_nil && val2.type!=vm_nil) {
|
&& 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 {
|
} else {
|
||||||
ctx.top[0] = (val1!=val2)? one:zero;
|
ctx.top[0] = (val1!=val2)? one:zero;
|
||||||
}
|
}
|
||||||
|
@ -504,7 +525,7 @@ inline void vm::o_neq() {
|
||||||
|
|
||||||
#define op_cmp(type)\
|
#define op_cmp(type)\
|
||||||
--ctx.top;\
|
--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_less() {op_cmp(<);}
|
||||||
inline void vm::o_leq() {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(>=);}
|
inline void vm::o_geq() {op_cmp(>=);}
|
||||||
|
|
||||||
#define op_cmp_const(type)\
|
#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_lessc() {op_cmp_const(<);}
|
||||||
inline void vm::o_leqc() {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() {
|
inline void vm::o_cnt() {
|
||||||
if (ctx.top[0].type!=vm_vec) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
(++ctx.top)[0] = var::cnt(-1);
|
(++ctx.top)[0] = var::cnt(-1);
|
||||||
|
@ -589,29 +612,29 @@ inline void vm::o_callv() {
|
||||||
var val = ctx.top[0];
|
var val = ctx.top[0];
|
||||||
var vec = (--ctx.top)[0];
|
var vec = (--ctx.top)[0];
|
||||||
if (vec.type==vm_vec) {
|
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) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
} else if (vec.type==vm_hash) {
|
} else if (vec.type==vm_hash) {
|
||||||
if (val.type!=vm_str) {
|
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;
|
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) {
|
if (ctx.top[0].type==vm_none) {
|
||||||
die("cannot find member \""+val.str()+"\"");
|
die(report_key_not_found(val.str(), vec.hash()));
|
||||||
return;
|
return;
|
||||||
} else if (ctx.top[0].type==vm_func) {
|
} else if (ctx.top[0].type==vm_func) {
|
||||||
ctx.top[0].func().local[0] = val; // 'me'
|
ctx.top[0].func().local[0] = val; // 'me'
|
||||||
}
|
}
|
||||||
} else if (vec.type==vm_str) {
|
} else if (vec.type==vm_str) {
|
||||||
auto& str = vec.str();
|
const auto& str = vec.str();
|
||||||
i32 num = val.tonum();
|
i32 num = val.to_num();
|
||||||
i32 len = str.length();
|
i32 len = str.length();
|
||||||
if (num<-len || num>=len) {
|
if (num<-len || num>=len) {
|
||||||
die("out of range:"+std::to_string(val.tonum()));
|
die(report_out_of_range(num, str.size()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ctx.top[0] = var::num(
|
ctx.top[0] = var::num(
|
||||||
|
@ -619,16 +642,16 @@ inline void vm::o_callv() {
|
||||||
);
|
);
|
||||||
} else if (vec.type==vm_map) {
|
} else if (vec.type==vm_map) {
|
||||||
if (val.type!=vm_str) {
|
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;
|
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) {
|
if (ctx.top[0].type==vm_none) {
|
||||||
die("cannot find symbol \""+val.str()+"\"");
|
die("cannot find symbol \""+val.str()+"\"");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
die("must call a vector/hash/string");
|
die("must call a vector/hash/string but get "+type_name_string(vec));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -636,13 +659,13 @@ inline void vm::o_callv() {
|
||||||
inline void vm::o_callvi() {
|
inline void vm::o_callvi() {
|
||||||
var val = ctx.top[0];
|
var val = ctx.top[0];
|
||||||
if (val.type!=vm_vec) {
|
if (val.type!=vm_vec) {
|
||||||
die("must use a vector");
|
die("must use a vector but get "+type_name_string(val));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// cannot use operator[],because this may cause overflow
|
// cannot use operator[],because this may cause overflow
|
||||||
(++ctx.top)[0] = val.vec().get_val(imm[ctx.pc]);
|
(++ctx.top)[0] = val.vec().get_value(imm[ctx.pc]);
|
||||||
if (ctx.top[0].type==vm_none) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -650,18 +673,18 @@ inline void vm::o_callvi() {
|
||||||
inline void vm::o_callh() {
|
inline void vm::o_callh() {
|
||||||
var val = ctx.top[0];
|
var val = ctx.top[0];
|
||||||
if (val.type!=vm_hash && val.type!=vm_map) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
const auto& str = cstr[imm[ctx.pc]];
|
const auto& str = const_string[imm[ctx.pc]];
|
||||||
if (val.type==vm_hash) {
|
if (val.type==vm_hash) {
|
||||||
ctx.top[0] = val.hash().get_val(str);
|
ctx.top[0] = val.hash().get_value(str);
|
||||||
} else {
|
} else {
|
||||||
ctx.top[0] = val.map().get_val(str);
|
ctx.top[0] = val.map().get_value(str);
|
||||||
}
|
}
|
||||||
if (ctx.top[0].type==vm_none) {
|
if (ctx.top[0].type==vm_none) {
|
||||||
val.type==vm_hash?
|
val.type==vm_hash?
|
||||||
die("member \"" + str + "\" does not exist"):
|
die(report_key_not_found(str, val.hash())):
|
||||||
die("cannot find symbol \"" + str + "\"");
|
die("cannot find symbol \"" + str + "\"");
|
||||||
return;
|
return;
|
||||||
} else if (ctx.top[0].type==vm_func) {
|
} else if (ctx.top[0].type==vm_func) {
|
||||||
|
@ -670,13 +693,13 @@ inline void vm::o_callh() {
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void vm::o_callfv() {
|
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
|
var* local = ctx.top-argc+1; // arguments begin address
|
||||||
if (local[-1].type!=vm_func) {
|
if (local[-1].type!=vm_func) {
|
||||||
die("must call a function");
|
die("must call a function but get "+type_name_string(local[-1]));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto& func = local[-1].func();
|
const auto& func = local[-1].func();
|
||||||
|
|
||||||
// swap funcr with local[-1]
|
// swap funcr with local[-1]
|
||||||
var tmp = local[-1];
|
var tmp = local[-1];
|
||||||
|
@ -684,27 +707,29 @@ inline void vm::o_callfv() {
|
||||||
ctx.funcr = tmp;
|
ctx.funcr = tmp;
|
||||||
|
|
||||||
// top-argc+lsize(local) +1(old pc) +1(old localr) +1(old upvalr)
|
// 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");
|
die("stack overflow");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// parameter size is func->psize-1, 1 is reserved for "me"
|
// parameter size is func->psize-1, 1 is reserved for "me"
|
||||||
u32 psize = func.psize-1;
|
const u32 parameter_size = func.parameter_size-1;
|
||||||
if (argc<psize && func.local[argc+1].type==vm_none) {
|
if (argc<parameter_size && func.local[argc+1].type==vm_none) {
|
||||||
die("lack argument(s)");
|
die(report_lack_arguments(argc, func));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// load dynamic argument, default nil, for better performance
|
||||||
var dynamic = nil;
|
var dynamic = nil;
|
||||||
if (func.dpara>=0) { // load dynamic arguments
|
if (func.dynamic_parameter_index>=0) {
|
||||||
|
// load dynamic argument
|
||||||
dynamic = ngc.alloc(vm_vec);
|
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]);
|
dynamic.vec().elems.push_back(local[i]);
|
||||||
}
|
}
|
||||||
} else if (psize<argc) {
|
} else if (parameter_size<argc) {
|
||||||
// load arguments to "arg", located at stack+1
|
// load arguments to default dynamic argument "arg", located at stack+1
|
||||||
dynamic = ngc.alloc(vm_vec);
|
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]);
|
dynamic.vec().elems.push_back(local[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -713,19 +738,21 @@ inline void vm::o_callfv() {
|
||||||
// then all the available values the vector needs
|
// then all the available values the vector needs
|
||||||
// are all outside the stack top and may be
|
// are all outside the stack top and may be
|
||||||
// collected incorrectly
|
// 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
|
for(u32 i = min_size; i>=1; --i) { // load arguments
|
||||||
local[i] = local[i-1];
|
local[i] = local[i-1];
|
||||||
}
|
}
|
||||||
local[0] = func.local[0];// load "me"
|
local[0] = func.local[0];// load "me"
|
||||||
|
|
||||||
// load local scope & default arguments
|
// load local scope & default arguments
|
||||||
for(u32 i = min_size+1; i<func.lsize; ++i) {
|
for(u32 i = min_size+1; i<func.local_size; ++i) {
|
||||||
local[i] = func.local[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] = ctx.upvalr;
|
||||||
(++ctx.top)[0] = var::addr(ctx.localr);
|
(++ctx.top)[0] = var::addr(ctx.localr);
|
||||||
|
@ -736,41 +763,48 @@ inline void vm::o_callfv() {
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void vm::o_callfh() {
|
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) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
auto& func = ctx.top[-1].func();
|
const auto& func = ctx.top[-1].func();
|
||||||
var tmp = ctx.top[-1];
|
var tmp = ctx.top[-1];
|
||||||
ctx.top[-1] = ctx.funcr;
|
ctx.top[-1] = ctx.funcr;
|
||||||
ctx.funcr = tmp;
|
ctx.funcr = tmp;
|
||||||
|
|
||||||
// top -1(hash) +lsize(local) +1(old pc) +1(old localr) +1(old upvalr)
|
// 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");
|
die("stack overflow");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (func.dpara>=0) {
|
// dynamic parameter is not allowed in this kind of function call
|
||||||
die("special call cannot use dynamic argument");
|
if (func.dynamic_parameter_index>=0) {
|
||||||
|
die("special call cannot use dynamic argument \"" +
|
||||||
|
const_string[func.dynamic_parameter_index] + "\""
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var* local = ctx.top;
|
var* local = ctx.top;
|
||||||
ctx.top += func.lsize;
|
ctx.top += func.local_size;
|
||||||
for(u32 i = 0; i<func.lsize; ++i) {
|
for(u32 i = 0; i<func.local_size; ++i) {
|
||||||
local[i] = func.local[i];
|
local[i] = func.local[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool lack_arguments_flag = false;
|
||||||
for(const auto& i : func.keys) {
|
for(const auto& i : func.keys) {
|
||||||
auto& key = cstr[i.first];
|
const auto& key = i.first;
|
||||||
if (hash.count(key)) {
|
if (hash.count(key)) {
|
||||||
local[i.second] = hash[key];
|
local[i.second] = hash.at(key);
|
||||||
} else if (local[i.second].type==vm_none) {
|
} else if (local[i.second].type==vm_none) {
|
||||||
die("lack argument(s): \""+key+"\"");
|
lack_arguments_flag = true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (lack_arguments_flag) {
|
||||||
|
die(report_special_call_lack_arguments(local, func));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ctx.top[0] = ctx.upvalr;
|
ctx.top[0] = ctx.upvalr;
|
||||||
(++ctx.top)[0] = var::addr(ctx.localr);
|
(++ctx.top)[0] = var::addr(ctx.localr);
|
||||||
|
@ -787,11 +821,12 @@ inline void vm::o_callb() {
|
||||||
|
|
||||||
// if running a native function about coroutine
|
// if running a native function about coroutine
|
||||||
// (top) will be set to another context.top, instead of main_context.top
|
// (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
|
// so we use tmp variable to store this return value
|
||||||
// and set it to top[0] later
|
// 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 get none, this means errors occurred when calling this native function
|
||||||
if (ctx.top[0].type==vm_none) {
|
if (ctx.top[0].type==vm_none) {
|
||||||
|
@ -808,7 +843,7 @@ inline void vm::o_slcbeg() {
|
||||||
// +--------------+
|
// +--------------+
|
||||||
(++ctx.top)[0] = ngc.alloc(vm_vec);
|
(++ctx.top)[0] = ngc.alloc(vm_vec);
|
||||||
if (ctx.top[-1].type!=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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -820,9 +855,9 @@ inline void vm::o_slcend() {
|
||||||
|
|
||||||
inline void vm::o_slc() {
|
inline void vm::o_slc() {
|
||||||
var val = (ctx.top--)[0];
|
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) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
ctx.top[0].vec().elems.push_back(res);
|
ctx.top[0].vec().elems.push_back(res);
|
||||||
|
@ -831,12 +866,12 @@ inline void vm::o_slc() {
|
||||||
inline void vm::o_slc2() {
|
inline void vm::o_slc2() {
|
||||||
var val2 = (ctx.top--)[0];
|
var val2 = (ctx.top--)[0];
|
||||||
var val1 = (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;
|
auto& aim = ctx.top[0].vec().elems;
|
||||||
|
|
||||||
u8 type1 = val1.type,type2=val2.type;
|
u8 type1 = val1.type,type2=val2.type;
|
||||||
i32 num1 = val1.tonum();
|
i32 num1 = val1.to_num();
|
||||||
i32 num2 = val2.tonum();
|
i32 num2 = val2.to_num();
|
||||||
i32 size = ref.size();
|
i32 size = ref.size();
|
||||||
if (type1==vm_nil && type2==vm_nil) {
|
if (type1==vm_nil && type2==vm_nil) {
|
||||||
num1 = 0;
|
num1 = 0;
|
||||||
|
@ -849,7 +884,9 @@ inline void vm::o_slc2() {
|
||||||
|
|
||||||
if (num1<-size || num1>=size || num2<-size || num2>=size) {
|
if (num1<-size || num1>=size || num2<-size || num2>=size) {
|
||||||
die("index " + std::to_string(num1) + ":" +
|
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;
|
return;
|
||||||
} else if (num1<=num2) {
|
} else if (num1<=num2) {
|
||||||
for(i32 i = num1; i<=num2; ++i) {
|
for(i32 i = num1; i<=num2; ++i) {
|
||||||
|
@ -875,8 +912,9 @@ inline void vm::o_mcalll() {
|
||||||
inline void vm::o_mupval() {
|
inline void vm::o_mupval() {
|
||||||
ctx.memr = &(
|
ctx.memr = &(
|
||||||
ctx.funcr.func()
|
ctx.funcr.func()
|
||||||
.upval[(imm[ctx.pc]>>16)&0xffff]
|
.upval[(imm[ctx.pc]>>16)&0xffff]
|
||||||
.upval()[imm[ctx.pc]&0xffff]);
|
.upval()[imm[ctx.pc]&0xffff]
|
||||||
|
);
|
||||||
(++ctx.top)[0] = ctx.memr[0];
|
(++ctx.top)[0] = ctx.memr[0];
|
||||||
// push value in this memory space on stack
|
// push value in this memory space on stack
|
||||||
// to avoid being garbage collected
|
// to avoid being garbage collected
|
||||||
|
@ -886,31 +924,31 @@ inline void vm::o_mcallv() {
|
||||||
var val = ctx.top[0]; // index
|
var val = ctx.top[0]; // index
|
||||||
var vec = (--ctx.top)[0]; // mcall vector, reserved on stack to avoid gc
|
var vec = (--ctx.top)[0]; // mcall vector, reserved on stack to avoid gc
|
||||||
if (vec.type==vm_vec) {
|
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) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
} else if (vec.type==vm_hash) { // do mcallh but use the mcallv way
|
} else if (vec.type==vm_hash) { // do mcallh but use the mcallv way
|
||||||
if (val.type!=vm_str) {
|
if (val.type!=vm_str) {
|
||||||
die("key must be string");
|
die("must use string as the key but get "+type_name_string(val));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto& ref = vec.hash();
|
auto& ref = vec.hash();
|
||||||
auto& str = val.str();
|
const auto& str = val.str();
|
||||||
ctx.memr = ref.get_mem(str);
|
ctx.memr = ref.get_memory(str);
|
||||||
if (!ctx.memr) {
|
if (!ctx.memr) {
|
||||||
ref.elems[str] = nil;
|
ref.elems[str] = nil;
|
||||||
ctx.memr = ref.get_mem(str);
|
ctx.memr = ref.get_memory(str);
|
||||||
}
|
}
|
||||||
} else if (vec.type==vm_map) {
|
} else if (vec.type==vm_map) {
|
||||||
if (val.type!=vm_str) {
|
if (val.type!=vm_str) {
|
||||||
die("key must be string");
|
die("must use string as the key but get "+type_name_string(val));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto& ref = vec.map();
|
auto& ref = vec.map();
|
||||||
auto& str = val.str();
|
const auto& str = val.str();
|
||||||
ctx.memr = ref.get_mem(str);
|
ctx.memr = ref.get_memory(str);
|
||||||
if (!ctx.memr) {
|
if (!ctx.memr) {
|
||||||
die("cannot find symbol \"" + str + "\"");
|
die("cannot find symbol \"" + str + "\"");
|
||||||
}
|
}
|
||||||
|
@ -923,22 +961,23 @@ inline void vm::o_mcallv() {
|
||||||
inline void vm::o_mcallh() {
|
inline void vm::o_mcallh() {
|
||||||
var hash = ctx.top[0]; // mcall hash, reserved on stack to avoid gc
|
var hash = ctx.top[0]; // mcall hash, reserved on stack to avoid gc
|
||||||
if (hash.type!=vm_hash && hash.type!=vm_map) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
auto& str = cstr[imm[ctx.pc]];
|
const auto& str = const_string[imm[ctx.pc]];
|
||||||
if (hash.type==vm_map) {
|
if (hash.type==vm_map) {
|
||||||
ctx.memr = hash.map().get_mem(str);
|
ctx.memr = hash.map().get_memory(str);
|
||||||
if (!ctx.memr) {
|
if (!ctx.memr) {
|
||||||
die("cannot find symbol \"" + str + "\"");
|
die("cannot find symbol \"" + str + "\"");
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto& ref = hash.hash();
|
auto& ref = hash.hash();
|
||||||
ctx.memr = ref.get_mem(str);
|
ctx.memr = ref.get_memory(str);
|
||||||
if (!ctx.memr) { // create a new key
|
// create a new key
|
||||||
|
if (!ctx.memr) {
|
||||||
ref.elems[str] = nil;
|
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.funcr = ctx.top[0];
|
||||||
ctx.top[0] = ret; // rewrite func with returned value
|
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& upval = up.upval();
|
||||||
auto size = func.func().lsize;
|
auto size = func.func().local_size;
|
||||||
upval.onstk = false;
|
upval.on_stack = false;
|
||||||
upval.elems.resize(size);
|
upval.elems.resize(size);
|
||||||
for(u32 i = 0; i<size; ++i) {
|
for(u32 i = 0; i<size; ++i) {
|
||||||
upval.elems[i] = local[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
|
// because there maybe another function call inside but return here
|
||||||
// coroutine function ends with setting pc to 0
|
// coroutine function ends with setting pc to 0
|
||||||
if (!ctx.pc) {
|
if (!ctx.pc) {
|
||||||
ngc.ctxreserve();
|
ngc.context_reserve();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
||||||
class optimizer:public ast_visitor {
|
class optimizer: public ast_visitor {
|
||||||
private:
|
private:
|
||||||
void const_string(binary_operator*, string_literal*, string_literal*);
|
void const_string(binary_operator*, string_literal*, string_literal*);
|
||||||
void const_number(binary_operator*, number_literal*, number_literal*);
|
void const_number(binary_operator*, number_literal*, number_literal*);
|
||||||
|
|
|
@ -111,7 +111,10 @@ void repl::execute() {
|
||||||
info::instance()->in_repl_mode = true;
|
info::instance()->in_repl_mode = true;
|
||||||
std::cout << "[nasal-repl] Initializating enviroment...\n";
|
std::cout << "[nasal-repl] Initializating enviroment...\n";
|
||||||
// run on pass for initializing basic modules, without output
|
// 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
|
// allow output now
|
||||||
runtime.set_allow_repl_output_flag(true);
|
runtime.set_allow_repl_output_flag(true);
|
||||||
std::cout << "[nasal-repl] Initialization complete.\n\n";
|
std::cout << "[nasal-repl] Initialization complete.\n\n";
|
||||||
|
|
|
@ -29,7 +29,7 @@ bool symbol_finder::visit_function(function* node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool symbol_finder::visit_iter_expr(iter_expr* node) {
|
bool symbol_finder::visit_iter_expr(iter_expr* node) {
|
||||||
if (node->get_name()) {
|
if (node->is_definition() && node->get_name()) {
|
||||||
symbols.push_back({
|
symbols.push_back({
|
||||||
node->get_name()->get_name(),
|
node->get_name()->get_name(),
|
||||||
node->get_name()->get_location()
|
node->get_name()->get_location()
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
namespace nasal {
|
namespace nasal {
|
||||||
|
|
||||||
class symbol_finder:public ast_visitor {
|
class symbol_finder: public ast_visitor {
|
||||||
public:
|
public:
|
||||||
struct symbol_info {
|
struct symbol_info {
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
|
@ -12,130 +12,130 @@ void dir_entry_destructor(void* ptr) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_pipe(var* local, gc& ngc) {
|
var builtin_pipe(context* ctx, gc* ngc) {
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
i32 fd[2];
|
i32 fd[2];
|
||||||
var res = ngc.alloc(vm_vec);
|
var res = ngc->alloc(vm_vec);
|
||||||
if (pipe(fd)==-1) {
|
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[0])));
|
||||||
res.vec().elems.push_back(var::num(static_cast<f64>(fd[1])));
|
res.vec().elems.push_back(var::num(static_cast<f64>(fd[1])));
|
||||||
return res;
|
return res;
|
||||||
#endif
|
#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
|
#ifndef _WIN32
|
||||||
f64 res=fork();
|
f64 res=fork();
|
||||||
if (res<0) {
|
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));
|
return var::num(static_cast<f64>(res));
|
||||||
#endif
|
#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 builtin_waitpid(context* ctx, gc* ngc) {
|
||||||
var pid = local[1];
|
auto pid = ctx->localr[1];
|
||||||
var nohang = local[2];
|
auto nohang = ctx->localr[2];
|
||||||
if (pid.type!=vm_num || nohang.type!=vm_num) {
|
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
|
#ifndef _WIN32
|
||||||
i32 ret_pid, status;
|
i32 ret_pid, status;
|
||||||
ret_pid = waitpid(pid.num(), &status, nohang.num()==0? 0:WNOHANG);
|
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>(ret_pid)));
|
||||||
vec.vec().elems.push_back(var::num(static_cast<f64>(status)));
|
vec.vec().elems.push_back(var::num(static_cast<f64>(status)));
|
||||||
return vec;
|
return vec;
|
||||||
#endif
|
#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 builtin_opendir(context* ctx, gc* ngc) {
|
||||||
var path = local[1];
|
auto path = ctx->localr[1];
|
||||||
if (path.type!=vm_str) {
|
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
|
#ifdef _MSC_VER
|
||||||
WIN32_FIND_DATAA data;
|
WIN32_FIND_DATAA data;
|
||||||
HANDLE p;
|
HANDLE p;
|
||||||
p = FindFirstFileA((path.str()+"\\*.*").c_str(), &data);
|
p = FindFirstFileA((path.str()+"\\*.*").c_str(), &data);
|
||||||
if (p==INVALID_HANDLE_VALUE) {
|
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
|
#else
|
||||||
DIR* p = opendir(path.str().c_str());
|
DIR* p = opendir(path.str().c_str());
|
||||||
if (!p) {
|
if (!p) {
|
||||||
return nas_err("opendir", "cannot open dir <"+path.str()+">");
|
return nas_err("unix::opendir", "cannot open dir <"+path.str()+">");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
var ret = ngc.alloc(vm_obj);
|
var ret = ngc->alloc(vm_obj);
|
||||||
ret.obj().set(dir_type_name, dir_entry_destructor, p);
|
ret.ghost().set(dir_type_name, dir_entry_destructor, p);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_readdir(var* local, gc& ngc) {
|
var builtin_readdir(context* ctx, gc* ngc) {
|
||||||
var handle = local[1];
|
auto handle = ctx->localr[1];
|
||||||
if (!handle.objchk(dir_type_name)) {
|
if (!handle.object_check(dir_type_name)) {
|
||||||
return nas_err("readdir", "not a valid dir handle");
|
return nas_err("unix::readdir", "not a valid dir handle");
|
||||||
}
|
}
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
WIN32_FIND_DATAA data;
|
WIN32_FIND_DATAA data;
|
||||||
if (!FindNextFileA(handle.obj().ptr,&data)) {
|
if (!FindNextFileA(handle.ghost().pointer, &data)) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
return ngc.newstr(data.cFileName);
|
return ngc->newstr(data.cFileName);
|
||||||
#else
|
#else
|
||||||
dirent* p = readdir(static_cast<DIR*>(handle.obj().ptr));
|
dirent* p = readdir(static_cast<DIR*>(handle.ghost().pointer));
|
||||||
return p? ngc.newstr(p->d_name):nil;
|
return p? ngc->newstr(p->d_name):nil;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_closedir(var* local, gc& ngc) {
|
var builtin_closedir(context* ctx, gc* ngc) {
|
||||||
var handle = local[1];
|
auto handle = ctx->localr[1];
|
||||||
if (!handle.objchk(dir_type_name)) {
|
if (!handle.object_check(dir_type_name)) {
|
||||||
return nas_err("closedir", "not a valid dir handle");
|
return nas_err("unix::closedir", "not a valid dir handle");
|
||||||
}
|
}
|
||||||
handle.obj().clear();
|
handle.ghost().clear();
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_chdir(var* local, gc& ngc) {
|
var builtin_chdir(context* ctx, gc* ngc) {
|
||||||
var path = local[1];
|
auto path = ctx->localr[1];
|
||||||
if (path.type!=vm_str) {
|
if (path.type!=vm_str) {
|
||||||
return var::num(-1.0);
|
return var::num(-1.0);
|
||||||
}
|
}
|
||||||
return var::num(static_cast<f64>(chdir(path.str().c_str())));
|
return var::num(static_cast<f64>(chdir(path.str().c_str())));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_environ(var* local, gc& ngc) {
|
var builtin_environ(context* ctx, gc* ngc) {
|
||||||
var res = ngc.temp = ngc.alloc(vm_vec);
|
var res = ngc->temp = ngc->alloc(vm_vec);
|
||||||
auto& vec = res.vec().elems;
|
auto& vec = res.vec().elems;
|
||||||
for(char** env = environ; *env; ++env) {
|
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;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_getcwd(var* local, gc& ngc) {
|
var builtin_getcwd(context* ctx, gc* ngc) {
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
if (!getcwd(buf, sizeof(buf))) {
|
if (!getcwd(buf, sizeof(buf))) {
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
return ngc.newstr(buf);
|
return ngc->newstr(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
var builtin_getenv(var* local, gc& ngc) {
|
var builtin_getenv(context* ctx, gc* ngc) {
|
||||||
var envvar = local[1];
|
auto envvar = ctx->localr[1];
|
||||||
if (envvar.type!=vm_str) {
|
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());
|
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[] = {
|
nasal_builtin_table unix_lib_native[] = {
|
||||||
|
|
|
@ -24,16 +24,16 @@ namespace nasal {
|
||||||
|
|
||||||
void dir_entry_destructor(void*);
|
void dir_entry_destructor(void*);
|
||||||
|
|
||||||
var builtin_pipe(var*, gc&);
|
var builtin_pipe(context*, gc*);
|
||||||
var builtin_fork(var*, gc&);
|
var builtin_fork(context*, gc*);
|
||||||
var builtin_waitpid(var*, gc&);
|
var builtin_waitpid(context*, gc*);
|
||||||
var builtin_opendir(var*, gc&);
|
var builtin_opendir(context*, gc*);
|
||||||
var builtin_readdir(var*, gc&);
|
var builtin_readdir(context*, gc*);
|
||||||
var builtin_closedir(var*, gc&);
|
var builtin_closedir(context*, gc*);
|
||||||
var builtin_chdir(var*, gc&);
|
var builtin_chdir(context*, gc*);
|
||||||
var builtin_environ(var*, gc&);
|
var builtin_environ(context*, gc*);
|
||||||
var builtin_getcwd(var*, gc&);
|
var builtin_getcwd(context*, gc*);
|
||||||
var builtin_getenv(var*, gc&);
|
var builtin_getenv(context*, gc*);
|
||||||
|
|
||||||
extern nasal_builtin_table unix_lib_native[];
|
extern nasal_builtin_table unix_lib_native[];
|
||||||
|
|
||||||
|
|
93
std/json.nas
93
std/json.nas
|
@ -2,8 +2,16 @@
|
||||||
# 2021 ValKmjolnir
|
# 2021 ValKmjolnir
|
||||||
|
|
||||||
var (
|
var (
|
||||||
_j_eof, _j_lbrace, _j_rbrace, _j_lbrkt, _j_rbrkt,
|
_j_eof,
|
||||||
_j_comma, _j_colon, _j_str, _j_num, _j_id
|
_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);
|
) = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||||
|
|
||||||
var _j_content = [
|
var _j_content = [
|
||||||
|
@ -19,7 +27,7 @@ var _j_content = [
|
||||||
"identifier"
|
"identifier"
|
||||||
];
|
];
|
||||||
|
|
||||||
var JSON = func() {
|
var parse = func() {
|
||||||
|
|
||||||
var text = "";
|
var text = "";
|
||||||
var line = 1;
|
var line = 1;
|
||||||
|
@ -66,7 +74,7 @@ var JSON = func() {
|
||||||
var get = func(str) {
|
var get = func(str) {
|
||||||
init();
|
init();
|
||||||
if (!size(str)) {
|
if (!size(str)) {
|
||||||
println("JSON.parse: empty string");
|
println("json::parse: empty string");
|
||||||
str = "[]";
|
str = "[]";
|
||||||
}
|
}
|
||||||
text = str;
|
text = str;
|
||||||
|
@ -146,28 +154,29 @@ var JSON = func() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var match = func(type) {
|
var match = func(type) {
|
||||||
if(token.type!=type)
|
if(token.type!=type) {
|
||||||
println("JSON.parse: line ",line,": expect ",_j_content[type]," but get `",token.content,"`.");
|
println("json::parse: line ",line,": expect ",_j_content[type]," but get `",token.content,"`.");
|
||||||
|
}
|
||||||
next();
|
next();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var member = func(hash) {
|
var member = func(hash) {
|
||||||
var name = token.content;
|
var name = token.content;
|
||||||
if(token.type==_j_rbrace) {
|
if (token.type==_j_rbrace) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(token.type==_j_str) {
|
if (token.type==_j_str) {
|
||||||
match(_j_str);
|
match(_j_str);
|
||||||
} else {
|
} else {
|
||||||
match(_j_id);
|
match(_j_id);
|
||||||
}
|
}
|
||||||
match(_j_colon);
|
match(_j_colon);
|
||||||
if(token.type==_j_lbrace) {
|
if (token.type==_j_lbrace) {
|
||||||
hash[name] = hash_gen();
|
hash[name] = hash_gen();
|
||||||
} elsif(token.type==_j_lbrkt) {
|
} elsif (token.type==_j_lbrkt) {
|
||||||
hash[name] = vec_gen();
|
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;
|
hash[name] = token.content;
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
@ -189,21 +198,21 @@ var JSON = func() {
|
||||||
var vec_gen = func() {
|
var vec_gen = func() {
|
||||||
var vec = [];
|
var vec = [];
|
||||||
match(_j_lbrkt);
|
match(_j_lbrkt);
|
||||||
if(token.type==_j_lbrace) {
|
if (token.type==_j_lbrace) {
|
||||||
append(vec, hash_gen());
|
append(vec, hash_gen());
|
||||||
} elsif(token.type==_j_lbrkt) {
|
} elsif (token.type==_j_lbrkt) {
|
||||||
append(vec, vec_gen());
|
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);
|
append(vec, token.content);
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
while(token.type==_j_comma) {
|
while(token.type==_j_comma) {
|
||||||
match(_j_comma);
|
match(_j_comma);
|
||||||
if(token.type==_j_lbrace) {
|
if (token.type==_j_lbrace) {
|
||||||
append(vec, hash_gen());
|
append(vec, hash_gen());
|
||||||
} elsif(token.type==_j_lbrkt) {
|
} elsif (token.type==_j_lbrkt) {
|
||||||
append(vec, vec_gen());
|
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);
|
append(vec, token.content);
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
@ -212,43 +221,43 @@ var JSON = func() {
|
||||||
return vec;
|
return vec;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return func(source) {
|
||||||
parse:func(str) {
|
if(typeof(source)!="str") {
|
||||||
if(typeof(str)!="str") {
|
println("json::parse: must use string but get", typeof(str));
|
||||||
println("JSON.parse: must use string");
|
return [];
|
||||||
return [];
|
|
||||||
}
|
|
||||||
get(str);
|
|
||||||
next();
|
|
||||||
|
|
||||||
if (token.type==_j_lbrkt) {
|
|
||||||
var res = vec_gen();
|
|
||||||
} else {
|
|
||||||
var res = hash_gen();
|
|
||||||
}
|
|
||||||
|
|
||||||
init();
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
get(source);
|
||||||
|
next();
|
||||||
|
|
||||||
|
if (token.type==_j_lbrkt) {
|
||||||
|
var res = vec_gen();
|
||||||
|
} else {
|
||||||
|
var res = hash_gen();
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}();
|
}();
|
||||||
|
|
||||||
JSON.stringify = func(object) {
|
var stringify = func(object) {
|
||||||
if(typeof(object)!="hash" and typeof(object)!="vec") {
|
var object_type = typeof(object);
|
||||||
println("JSON.stringify: must use hashmap or vector");
|
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 "[]";
|
return "[]";
|
||||||
}
|
}
|
||||||
|
|
||||||
var s = "";
|
var s = "";
|
||||||
var gen = func(elem) {
|
var gen = func(elem) {
|
||||||
var t = typeof(elem);
|
var t = typeof(elem);
|
||||||
if(t=="num") {
|
if (t=="num") {
|
||||||
s ~= str(elem);
|
s ~= str(elem);
|
||||||
} elsif(t=="str") {
|
} elsif (t=="str") {
|
||||||
s ~= '"'~elem~'"';
|
s ~= '"'~elem~'"';
|
||||||
} elsif(t=="vec") {
|
} elsif (t=="vec") {
|
||||||
vgen(elem);
|
vgen(elem);
|
||||||
} elsif(t=="hash") {
|
} elsif (t=="hash") {
|
||||||
hgen(elem);
|
hgen(elem);
|
||||||
} else {
|
} else {
|
||||||
s ~= '"undefined"';
|
s ~= '"undefined"';
|
||||||
|
|
40
std/lib.nas
40
std/lib.nas
|
@ -75,6 +75,10 @@ var floor = func(val) {
|
||||||
return __floor(val);
|
return __floor(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ceil = func(val) {
|
||||||
|
return __ceil(val);
|
||||||
|
}
|
||||||
|
|
||||||
# exit using std::exit
|
# exit using std::exit
|
||||||
var exit = func(val = -1) {
|
var exit = func(val = -1) {
|
||||||
return __exit(val);
|
return __exit(val);
|
||||||
|
@ -228,19 +232,21 @@ var println = func(elems...) {
|
||||||
|
|
||||||
# sort function using quick sort
|
# sort function using quick sort
|
||||||
# not very efficient... :(
|
# not very efficient... :(
|
||||||
var sort = func(){
|
var sort = func() {
|
||||||
srand(); # be aware! this causes global changes
|
srand(); # be aware! this causes global changes
|
||||||
var quick_sort_core = func(vec, left, right, cmp) {
|
var quick_sort_core = func(vec, left, right, cmp) {
|
||||||
if(left>=right) return nil;
|
if(left>=right) return nil;
|
||||||
var base = left+int(rand()*(right-left));
|
var base = left+int(rand()*(right-left));
|
||||||
(vec[left], vec[base]) = (vec[base], vec[left]);
|
(vec[left], vec[base]) = (vec[base], vec[left]);
|
||||||
var (i, j, tmp) = (left, right, vec[left]);
|
var (i, j, tmp) = (left, right, vec[left]);
|
||||||
while(i<j){
|
while(i<j) {
|
||||||
while(i<j and cmp(tmp,vec[j]))
|
while(i<j and cmp(tmp,vec[j])) {
|
||||||
j -= 1;
|
j -= 1;
|
||||||
|
}
|
||||||
vec[i] = vec[j];
|
vec[i] = vec[j];
|
||||||
while(i<j and cmp(vec[i],tmp))
|
while(i<j and cmp(vec[i],tmp)) {
|
||||||
i += 1;
|
i += 1;
|
||||||
|
}
|
||||||
vec[j] = vec[i];
|
vec[j] = vec[i];
|
||||||
j -= 1;
|
j -= 1;
|
||||||
}
|
}
|
||||||
|
@ -249,7 +255,7 @@ var sort = func(){
|
||||||
quick_sort_core(vec, i+1, right, cmp);
|
quick_sort_core(vec, i+1, right, cmp);
|
||||||
return nil;
|
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);
|
quick_sort_core(vec, 0, size(vec)-1, cmp);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
@ -276,7 +282,7 @@ var isnum = func(x) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var isscalar = func(s) {
|
var isscalar = func(s) {
|
||||||
var t=typeof(s);
|
var t = typeof(s);
|
||||||
return (t=="num" or t=="str")? 1:0;
|
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
|
# get the index of val in the vec
|
||||||
var vecindex = func(vec,val) {
|
var vecindex = func(vec, val) {
|
||||||
forindex(var i;vec)
|
forindex(var i; vec) {
|
||||||
if(val==vec[i])
|
if(val==vec[i]) {
|
||||||
return i;
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
# check if the object is an instance of the class
|
# check if the object is an instance of the class
|
||||||
var isa = func(object, class) {
|
var isa = func(object, class) {
|
||||||
if (!ishash(object)) {
|
if (!ishash(object)) {
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
if(!contains(object, "parents") or !isvec(object.parents)) {
|
if(!contains(object, "parents") or !isvec(object.parents)) {
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
foreach(var elem;object.parents)
|
foreach(var elem; object.parents) {
|
||||||
if(elem==class or isa(elem, class))
|
if(elem==class or isa(elem, class)) {
|
||||||
return 1;
|
return true;
|
||||||
return 0;
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
# assert aborts when condition is not true
|
# assert aborts when condition is not true
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
# result.nas
|
# result.nas
|
||||||
# ValKmjolnir 2021
|
# ValKmjolnir 2021
|
||||||
|
|
||||||
var Result=func(){
|
var new = func() {
|
||||||
var (ok,err,flag)=(nil,"",1);
|
var (ok, err, flag) = (nil, "", 1);
|
||||||
return{
|
return {
|
||||||
Ok:func(val){
|
Ok: func(val) {
|
||||||
ok=val;
|
ok = val;
|
||||||
flag=0;
|
flag = 0;
|
||||||
return me;
|
return me;
|
||||||
},
|
},
|
||||||
Err:func(info){
|
Err: func(info) {
|
||||||
err=info;
|
err = info;
|
||||||
flag=1;
|
flag = 1;
|
||||||
return me;
|
return me;
|
||||||
},
|
},
|
||||||
unwrap:func(){
|
unwrap: func() {
|
||||||
if(flag)
|
if (flag) {
|
||||||
die(err);
|
die(err);
|
||||||
|
}
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -191,7 +191,6 @@ var ansi_escape_sequence=func(){
|
||||||
}
|
}
|
||||||
unix.sleep(0.01);
|
unix.sleep(0.01);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# enable unicode
|
# enable unicode
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -1,9 +1,7 @@
|
||||||
import.std.json;
|
import.std.json;
|
||||||
import.std.process_bar;
|
import.std.process_bar;
|
||||||
|
|
||||||
var JSON = json.JSON;
|
var ss = json.stringify({
|
||||||
|
|
||||||
var ss=JSON.stringify({
|
|
||||||
vec:[0,1,2],
|
vec:[0,1,2],
|
||||||
hash:{
|
hash:{
|
||||||
m1:0,
|
m1:0,
|
||||||
|
@ -17,10 +15,11 @@ var ss=JSON.stringify({
|
||||||
empty_an:[[[[[[{}]]]]]],
|
empty_an:[[[[[[{}]]]]]],
|
||||||
function:func(){}
|
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],
|
vec:[0,1,2,3],
|
||||||
hash:{
|
hash:{
|
||||||
m1:0,
|
m1:0,
|
||||||
|
@ -33,12 +32,13 @@ var ss=JSON.stringify([{
|
||||||
empty_an:[[[[[{}]]]]],
|
empty_an:[[[[[{}]]]]],
|
||||||
function:func(){}
|
function:func(){}
|
||||||
}]);
|
}]);
|
||||||
println(ss,"\n");
|
|
||||||
println(JSON.parse(ss),"\n");
|
println(ss, "\n");
|
||||||
|
println(json.parse(ss), "\n");
|
||||||
|
|
||||||
func {
|
func {
|
||||||
var bar=process_bar.high_resolution_bar(30);
|
var bar = process_bar.high_resolution_bar(30);
|
||||||
var tmp=[
|
var tmp = [
|
||||||
{t0:nil},
|
{t0:nil},
|
||||||
{t1:nil},
|
{t1:nil},
|
||||||
{t2:nil},
|
{t2:nil},
|
||||||
|
@ -50,18 +50,18 @@ func {
|
||||||
];
|
];
|
||||||
|
|
||||||
srand();
|
srand();
|
||||||
foreach(var h;tmp) {
|
foreach(var h; tmp) {
|
||||||
var name=keys(h)[0];
|
var name = keys(h)[0];
|
||||||
h[name]=[];
|
h[name] = [];
|
||||||
print("\e[1000D",bar.bar(0));
|
print("\e[1000D", bar.bar(0));
|
||||||
for(var i=0;i<500;i+=1) {
|
for(var i = 0; i<500; i+=1) {
|
||||||
append(h[name],{id:i,content:int(rand()*1e7)});
|
append(h[name], {id:i, content:int(rand()*1e7)});
|
||||||
print("\e[1000D",bar.bar((i+1)/500));
|
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");
|
print("\e[1000D", "\e["~str(size(tmp))~"A");
|
||||||
foreach(var h;JSON.parse(JSON.stringify(tmp))) {
|
foreach(var h; json.parse(json.stringify(tmp))) {
|
||||||
println("\e[1000D",bar.bar(1)," parse done ",keys(h)[0]," ",size(h[keys(h)[0]]));
|
println("\e[1000D", bar.bar(1), " parse done ", keys(h)[0], " ", size(h[keys(h)[0]]));
|
||||||
}
|
}
|
||||||
}();
|
}();
|
|
@ -2,7 +2,6 @@ import.module.libsock;
|
||||||
import.std.json;
|
import.std.json;
|
||||||
import.std.runtime;
|
import.std.runtime;
|
||||||
|
|
||||||
var JSON = json.JSON;
|
|
||||||
var socket = libsock.socket;
|
var socket = libsock.socket;
|
||||||
|
|
||||||
var gettime=func(){
|
var gettime=func(){
|
||||||
|
@ -79,19 +78,19 @@ var server=func(ip,port) {
|
||||||
while(1) {
|
while(1) {
|
||||||
var data=jsonRPC.recv(client);
|
var data=jsonRPC.recv(client);
|
||||||
if (data!=nil) {
|
if (data!=nil) {
|
||||||
data=JSON.parse(data);
|
data=json.parse(data);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (contains(methods,data.method)) {
|
if (contains(methods,data.method)) {
|
||||||
jsonRPC.send(client, JSON.stringify({
|
jsonRPC.send(client, json.stringify({
|
||||||
jsonrpc:2.0,
|
jsonrpc:2.0,
|
||||||
id:data.id,
|
id:data.id,
|
||||||
error:"null",
|
error:"null",
|
||||||
result:methods[data.method](data.params)
|
result:methods[data.method](data.params)
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
jsonRPC.send(client, JSON.stringify({
|
jsonRPC.send(client, json.stringify({
|
||||||
jsonrpc:2.0,
|
jsonrpc:2.0,
|
||||||
id:data.id,
|
id:data.id,
|
||||||
error:"no such method \\\""~data.method~"\\\"",
|
error:"no such method \\\""~data.method~"\\\"",
|
||||||
|
@ -113,13 +112,13 @@ var client=func(ip,port) {
|
||||||
var server=jsonRPC.connect(ip,port);
|
var server=jsonRPC.connect(ip,port);
|
||||||
while(1) {
|
while(1) {
|
||||||
unix.sleep(5);
|
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);
|
jsonRPC.send(server, data);
|
||||||
var respond=jsonRPC.recv(server);
|
var respond=jsonRPC.recv(server);
|
||||||
if (respond==nil) {
|
if (respond==nil) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
println("[",gettime(),"] result: ",JSON.parse(respond).result);
|
println("[",gettime(),"] result: ",json.parse(respond).result);
|
||||||
call_id+=1;
|
call_id+=1;
|
||||||
}
|
}
|
||||||
jsonRPC.disconnect(server);
|
jsonRPC.disconnect(server);
|
||||||
|
|
|
@ -234,4 +234,19 @@ for(var a=0;a<16;a+=1) {
|
||||||
}
|
}
|
||||||
|
|
||||||
print([0, 1, 2]~[3, 4, 5], "\n");
|
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);
|
||||||
|
}
|
|
@ -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);
|
Loading…
Reference in New Issue