forked from xxq250/Nasal-Interpreter
Merge pull request #27 from ValKmjolnir/develop
🎨 bug fix & add experimental REPL & optimize framework of vm
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -48,6 +48,7 @@ nasal.exe
|
||||
.vscode
|
||||
dump
|
||||
fgfs.log
|
||||
.temp.*
|
||||
|
||||
# build dir
|
||||
build
|
||||
|
||||
@@ -38,7 +38,8 @@ set(NASAL_OBJECT_SOURCE_FILE
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_parse.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/nasal_vm.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/optimizer.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/symbol_finder.cpp)
|
||||
${CMAKE_SOURCE_DIR}/src/symbol_finder.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/repl.cpp)
|
||||
add_library(nasal-object STATIC ${NASAL_OBJECT_SOURCE_FILE})
|
||||
target_include_directories(nasal-object PRIVATE ${CMAKE_SOURCE_DIR}/src)
|
||||
|
||||
|
||||
10
makefile
10
makefile
@@ -23,7 +23,8 @@ NASAL_HEADER=\
|
||||
src/math_lib.h\
|
||||
src/dylib_lib.h\
|
||||
src/unix_lib.h\
|
||||
src/coroutine.h
|
||||
src/coroutine.h\
|
||||
src/repl.h
|
||||
|
||||
NASAL_OBJECT=\
|
||||
build/nasal_err.o\
|
||||
@@ -49,6 +50,7 @@ NASAL_OBJECT=\
|
||||
build/coroutine.o\
|
||||
build/nasal_vm.o\
|
||||
build/nasal_dbg.o\
|
||||
build/repl.o\
|
||||
build/main.o
|
||||
|
||||
# for test
|
||||
@@ -67,7 +69,10 @@ build/main.o: $(NASAL_HEADER) src/main.cpp | build
|
||||
build/nasal_misc.o: src/nasal.h src/nasal_misc.cpp | build
|
||||
$(CXX) -std=$(STD) -c -O3 src/nasal_misc.cpp -fno-exceptions -fPIC -o build/nasal_misc.o -I .
|
||||
|
||||
build/nasal_err.o: src/nasal.h src/nasal_err.h src/nasal_err.cpp | build
|
||||
build/repl.o: src/nasal.h src/repl.h src/repl.cpp | build
|
||||
$(CXX) -std=$(STD) -c -O3 src/repl.cpp -fno-exceptions -fPIC -o build/repl.o -I .
|
||||
|
||||
build/nasal_err.o: src/nasal.h src/repl.h src/nasal_err.h src/nasal_err.cpp | build
|
||||
$(CXX) -std=$(STD) -c -O3 src/nasal_err.cpp -fno-exceptions -fPIC -o build/nasal_err.o -I .
|
||||
|
||||
build/nasal_gc.o: src/nasal.h src/nasal_gc.h src/nasal_gc.cpp | build
|
||||
@@ -83,6 +88,7 @@ build/nasal_import.o: \
|
||||
|
||||
build/nasal_lexer.o: \
|
||||
src/nasal.h\
|
||||
src/repl.h\
|
||||
src/nasal_err.h\
|
||||
src/nasal_lexer.h src/nasal_lexer.cpp | build
|
||||
$(CXX) -std=$(STD) -c -O3 src/nasal_lexer.cpp -fno-exceptions -fPIC -o build/nasal_lexer.o -I .
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include <winsock.h>
|
||||
#pragma comment(lib,"ws2_32")
|
||||
|
||||
class WSAmanager{
|
||||
class WSAmanager {
|
||||
private:
|
||||
WSAData data;
|
||||
public:
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
bool ast_dumper::visit_null_expr(null_expr* node) {
|
||||
dump_indent();
|
||||
std::cout << "null" << format_location(node->get_location());
|
||||
@@ -478,4 +480,6 @@ bool ast_dumper::visit_return_expr(return_expr* node) {
|
||||
pop_indent();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class ast_dumper:public ast_visitor {
|
||||
private:
|
||||
std::vector<std::string> indent;
|
||||
@@ -78,4 +80,6 @@ public:
|
||||
void dump(code_block* root) {
|
||||
root->accept(this);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "ast_visitor.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
bool ast_visitor::visit_expr(expr* node) {
|
||||
node->accept(this);
|
||||
return true;
|
||||
@@ -231,4 +233,6 @@ bool ast_visitor::visit_return_expr(return_expr* node) {
|
||||
node->get_value()->accept(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include "nasal_ast.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class ast_visitor {
|
||||
public:
|
||||
virtual bool visit_expr(expr*);
|
||||
@@ -40,4 +42,6 @@ public:
|
||||
virtual bool visit_continue_expr(continue_expr*);
|
||||
virtual bool visit_break_expr(break_expr*);
|
||||
virtual bool visit_return_expr(return_expr*);
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
54
src/main.cpp
54
src/main.cpp
@@ -11,21 +11,22 @@
|
||||
#include "nasal_codegen.h"
|
||||
#include "nasal_vm.h"
|
||||
#include "nasal_dbg.h"
|
||||
#include "repl.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <thread>
|
||||
#include <cstdlib>
|
||||
|
||||
const u32 VM_RAW_AST = 1;
|
||||
const u32 VM_AST = 1<<1;
|
||||
const u32 VM_CODE = 1<<2;
|
||||
const u32 VM_TIME = 1<<3;
|
||||
const u32 VM_EXEC = 1<<4;
|
||||
const u32 VM_DETAIL = 1<<5;
|
||||
const u32 VM_DEBUG = 1<<6;
|
||||
const u32 VM_SYMINFO = 1<<7;
|
||||
const u32 VM_PROFILE = 1<<8;
|
||||
const u32 VM_PROF_ALL = 1<<9;
|
||||
const u32 VM_RAW_AST = 1;
|
||||
const u32 VM_AST = 1<<1;
|
||||
const u32 VM_CODE = 1<<2;
|
||||
const u32 VM_TIME = 1<<3;
|
||||
const u32 VM_EXEC = 1<<4;
|
||||
const u32 VM_DETAIL = 1<<5;
|
||||
const u32 VM_DEBUG = 1<<6;
|
||||
const u32 VM_SYMINFO = 1<<7;
|
||||
const u32 VM_PROFILE = 1<<8;
|
||||
const u32 VM_PROF_ALL = 1<<9;
|
||||
|
||||
std::ostream& help(std::ostream& out) {
|
||||
out
|
||||
@@ -40,6 +41,7 @@ std::ostream& help(std::ostream& out) {
|
||||
<< "option:\n"
|
||||
<< " -h, --help | get help.\n"
|
||||
<< " -v, --version | get version.\n"
|
||||
<< " -r, --repl | use repl interpreter(experimental).\n"
|
||||
<< "\nnasal [option] <file> [argv]\n"
|
||||
<< "option:\n"
|
||||
<< " -a, --ast | view ast after link/optimize process.\n"
|
||||
@@ -87,7 +89,7 @@ std::ostream& version(std::ostream& out) {
|
||||
num = (num+rand())*(1.0/(RAND_MAX+1.0));
|
||||
}
|
||||
if (num<0.01) {
|
||||
parse::easter_egg();
|
||||
nasal::parse::easter_egg();
|
||||
}
|
||||
out << "nasal interpreter version " << __nasver;
|
||||
out << " (" << __DATE__ << " " << __TIME__ << ")\n";
|
||||
@@ -110,11 +112,10 @@ void execute(
|
||||
using clk = std::chrono::high_resolution_clock;
|
||||
const auto den = clk::duration::period::den;
|
||||
|
||||
lexer lex;
|
||||
parse parse;
|
||||
linker ld;
|
||||
codegen gen;
|
||||
vm ctx;
|
||||
nasal::lexer lex;
|
||||
nasal::parse parse;
|
||||
nasal::linker ld;
|
||||
nasal::codegen gen;
|
||||
|
||||
// lexer scans file to get tokens
|
||||
lex.scan(file).chkerr();
|
||||
@@ -122,7 +123,7 @@ void execute(
|
||||
// parser gets lexer's token list to compile
|
||||
parse.compile(lex).chkerr();
|
||||
if (cmd&VM_RAW_AST) {
|
||||
auto dumper = std::unique_ptr<ast_dumper>(new ast_dumper);
|
||||
auto dumper = std::unique_ptr<nasal::ast_dumper>(new nasal::ast_dumper);
|
||||
dumper->dump(parse.tree());
|
||||
}
|
||||
|
||||
@@ -130,11 +131,10 @@ void execute(
|
||||
ld.link(parse, file, cmd&VM_DETAIL).chkerr();
|
||||
|
||||
// optimizer does simple optimization on ast
|
||||
auto opt = new optimizer;
|
||||
auto opt = std::unique_ptr<nasal::optimizer>(new nasal::optimizer);
|
||||
opt->do_optimization(parse.tree());
|
||||
delete opt;
|
||||
if (cmd&VM_AST) {
|
||||
auto dumper = std::unique_ptr<ast_dumper>(new ast_dumper);
|
||||
auto dumper = std::unique_ptr<nasal::ast_dumper>(new nasal::ast_dumper);
|
||||
dumper->dump(parse.tree());
|
||||
}
|
||||
|
||||
@@ -150,15 +150,18 @@ void execute(
|
||||
// run
|
||||
auto start = clk::now();
|
||||
if (cmd&VM_DEBUG) {
|
||||
dbg().run(gen, ld, argv, cmd&VM_PROFILE, cmd&VM_PROF_ALL);
|
||||
auto debugger = std::unique_ptr<nasal::dbg>(new nasal::dbg);
|
||||
debugger->run(gen, ld, argv, cmd&VM_PROFILE, cmd&VM_PROF_ALL);
|
||||
} else if (cmd&VM_TIME || cmd&VM_EXEC) {
|
||||
ctx.run(gen, ld, argv, cmd&VM_DETAIL);
|
||||
auto runtime = std::unique_ptr<nasal::vm>(new nasal::vm);
|
||||
runtime->run(gen, ld, argv, cmd&VM_DETAIL);
|
||||
}
|
||||
|
||||
// get running time
|
||||
auto end = clk::now();
|
||||
if (cmd&VM_TIME) {
|
||||
f64 tm = (clk::now()-start).count()*1.0/den;
|
||||
std::clog << "process exited after " << tm << "s.\n\n";
|
||||
std::clog << "process exited after ";
|
||||
std::clog << (end-start).count()*1.0/den << "s.\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,6 +179,9 @@ i32 main(i32 argc, const char* argv[]) {
|
||||
std::clog << help;
|
||||
} else if (s=="-v" || s=="--version") {
|
||||
std::clog << version;
|
||||
} else if (s=="-r" || s=="--repl") {
|
||||
auto repl = std::unique_ptr<nasal::repl::repl>(new nasal::repl::repl);
|
||||
repl->execute();
|
||||
} else if (s[0]!='-') {
|
||||
execute(s, {}, VM_EXEC);
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "nasal_ast.h"
|
||||
#include "ast_visitor.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_expr(this);
|
||||
}
|
||||
@@ -365,4 +367,6 @@ return_expr::~return_expr() {
|
||||
|
||||
void return_expr::accept(ast_visitor* visitor) {
|
||||
visitor->visit_return_expr(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
enum class expr_type:u32 {
|
||||
ast_null = 0, // null node
|
||||
ast_block, // code block
|
||||
@@ -669,3 +671,5 @@ public:
|
||||
expr* get_value() {return value;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "nasal_codegen.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void codegen::init_file_map(const std::vector<std::string>& file_list) {
|
||||
file_map = {};
|
||||
for(usize i = 0; i<file_list.size(); ++i) {
|
||||
@@ -75,11 +77,14 @@ void codegen::regist_str(const std::string& str) {
|
||||
void codegen::find_symbol(code_block* node) {
|
||||
auto finder = std::unique_ptr<symbol_finder>(new symbol_finder);
|
||||
for(const auto& i : finder->do_find(node)) {
|
||||
if (!experimental_namespace.count(i.file)) {
|
||||
experimental_namespace[i.file] = {};
|
||||
if (native_function_mapper.count(i.name)) {
|
||||
die("definition conflicts with native function", i.location);
|
||||
}
|
||||
if (local.empty() && !experimental_namespace.at(i.file).count(i.name)) {
|
||||
experimental_namespace.at(i.file).insert(i.name);
|
||||
if (!experimental_namespace.count(i.location.file)) {
|
||||
experimental_namespace[i.location.file] = {};
|
||||
}
|
||||
if (local.empty() && !experimental_namespace.at(i.location.file).count(i.name)) {
|
||||
experimental_namespace.at(i.location.file).insert(i.name);
|
||||
}
|
||||
add_symbol(i.name);
|
||||
}
|
||||
@@ -1141,7 +1146,7 @@ void codegen::ret_gen(return_expr* node) {
|
||||
gen(op_ret, 0, node->get_location());
|
||||
}
|
||||
|
||||
const error& codegen::compile(parse& parse, linker& import, bool repl) {
|
||||
const error& codegen::compile(parse& parse, linker& import) {
|
||||
init_native_function();
|
||||
init_file_map(import.get_file_list());
|
||||
|
||||
@@ -1154,7 +1159,7 @@ const error& codegen::compile(parse& parse, linker& import, bool repl) {
|
||||
|
||||
// search global symbols first
|
||||
find_symbol(parse.tree());
|
||||
gen(op_intg, repl? STACK_DEPTH/2:global.size(), parse.tree()->get_location());
|
||||
gen(op_intg, global.size(), parse.tree()->get_location());
|
||||
|
||||
// generate main block
|
||||
block_gen(parse.tree());
|
||||
@@ -1252,3 +1257,5 @@ void codegen::symbol_dump(std::ostream& out) const {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#pragma warning (disable:4267)
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class codegen {
|
||||
private:
|
||||
error err;
|
||||
@@ -132,7 +134,9 @@ public:
|
||||
|
||||
public:
|
||||
codegen() = default;
|
||||
const error& compile(parse&, linker&, bool repl = false);
|
||||
const error& compile(parse&, linker&);
|
||||
void print(std::ostream&);
|
||||
void symbol_dump(std::ostream&) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "nasal_dbg.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void debug_prof_data::init_counter() {
|
||||
for(usize i = 0; i<debug_prof_data::operand_size; ++i) {
|
||||
operand_counter[i] = 0;
|
||||
@@ -276,3 +278,5 @@ void dbg::run(
|
||||
imm.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class debug_prof_data {
|
||||
private:
|
||||
static const usize operand_size = op_code_type::op_ret + 1;
|
||||
@@ -167,3 +169,5 @@ public:
|
||||
bool
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
#include "nasal_err.h"
|
||||
#include "repl.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h> // use SetConsoleTextAttribute
|
||||
struct for_reset {
|
||||
@@ -6,7 +10,11 @@ struct for_reset {
|
||||
for_reset() {
|
||||
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &scr);
|
||||
}
|
||||
} reset_ter_color;
|
||||
static for_reset* singleton() {
|
||||
static for_reset windows_set;
|
||||
return &windows_set;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
std::ostream& back_white(std::ostream& s) {
|
||||
@@ -57,7 +65,7 @@ std::ostream& white(std::ostream& s) {
|
||||
std::ostream& reset(std::ostream& s) {
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),
|
||||
reset_ter_color.scr.wAttributes);
|
||||
for_reset::singleton()->scr.wAttributes);
|
||||
#else
|
||||
s << "\033[0m";
|
||||
#endif
|
||||
@@ -68,7 +76,24 @@ void flstream::load(const std::string& f) {
|
||||
if (file==f) { // don't need to load a loaded file
|
||||
return;
|
||||
} else {
|
||||
file=f;
|
||||
file = f;
|
||||
}
|
||||
|
||||
if (repl::info::instance()->in_repl_mode &&
|
||||
repl::info::instance()->repl_file_name==file) {
|
||||
const auto& source = repl::info::instance()->repl_file_source;
|
||||
res = {};
|
||||
size_t pos = 0, last = 0;
|
||||
while ((pos = source.find("\n", last))!=std::string::npos) {
|
||||
res.push_back(source.substr(last, pos - last));
|
||||
last = pos + 1;
|
||||
}
|
||||
if (last<source.length()) {
|
||||
res.push_back(source.substr(last));
|
||||
} else {
|
||||
res.push_back("");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
res.clear();
|
||||
@@ -85,11 +110,6 @@ void flstream::load(const std::string& f) {
|
||||
}
|
||||
}
|
||||
|
||||
void error::fatal(const std::string& stage, const std::string& info) {
|
||||
std::cerr << red << stage << ": " << white << info << reset << "\n\n";
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
void error::err(const std::string& stage, const std::string& info) {
|
||||
++cnt;
|
||||
std::cerr << red << stage << ": " << white << info << reset << "\n\n";
|
||||
@@ -166,4 +186,6 @@ void error::err(
|
||||
}
|
||||
}
|
||||
std::cerr << "\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
#include "nasal.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
struct span {
|
||||
u32 begin_line;
|
||||
u32 begin_column;
|
||||
@@ -54,7 +56,6 @@ private:
|
||||
|
||||
public:
|
||||
error():cnt(0) {}
|
||||
void fatal(const std::string&, const std::string&);
|
||||
void err(const std::string&, const std::string&);
|
||||
void warn(const std::string&, const std::string&);
|
||||
void err(const std::string&, const span&, const std::string&);
|
||||
@@ -66,3 +67,5 @@ public:
|
||||
}
|
||||
u32 geterr() const {return cnt;}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -107,9 +107,19 @@ void nas_ghost::set(
|
||||
}
|
||||
|
||||
void nas_ghost::clear() {
|
||||
if (!ptr || !dtor_ptr) {
|
||||
// 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;
|
||||
@@ -123,17 +133,20 @@ std::ostream& operator<<(std::ostream& out, const nas_ghost& ghost) {
|
||||
}
|
||||
|
||||
void nas_co::clear() {
|
||||
for(u32 i = 0; i<STACK_DEPTH; ++i) {
|
||||
stack[i] = var::nil();
|
||||
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 = stack+STACK_DEPTH-1;
|
||||
ctx.top = stack;
|
||||
ctx.canary = ctx.stack+STACK_DEPTH-1;
|
||||
ctx.top = ctx.stack;
|
||||
ctx.funcr = var::nil();
|
||||
ctx.upvalr = var::nil();
|
||||
ctx.stack = stack;
|
||||
|
||||
status = status::suspended;
|
||||
}
|
||||
@@ -393,7 +406,13 @@ void gc::concurrent_mark(std::vector<var>& vec, usize begin, usize end) {
|
||||
}
|
||||
|
||||
void gc::mark_context_root(std::vector<var>& bfs_queue) {
|
||||
|
||||
// scan global
|
||||
for(usize i = 0; i<main_context_global_size; ++i) {
|
||||
auto& val = main_context_global[i];
|
||||
if (val.type>vm_num) {
|
||||
bfs_queue.push_back(val);
|
||||
}
|
||||
}
|
||||
// scan now running context, this context maybe related to coroutine or main
|
||||
for(var* i = rctx->stack; i<=rctx->top; ++i) {
|
||||
if (i->type>vm_num) {
|
||||
@@ -469,7 +488,7 @@ void gc::mark_upval(std::vector<var>& bfs_queue, nas_upval& upval) {
|
||||
void gc::mark_co(std::vector<var>& bfs_queue, nas_co& co) {
|
||||
bfs_queue.push_back(co.ctx.funcr);
|
||||
bfs_queue.push_back(co.ctx.upvalr);
|
||||
for(var* i = co.stack; i<=co.ctx.top; ++i) {
|
||||
for(var* i = co.ctx.stack; i<=co.ctx.top; ++i) {
|
||||
if (i->type>vm_num) {
|
||||
bfs_queue.push_back(*i);
|
||||
}
|
||||
|
||||
@@ -202,14 +202,14 @@ public:
|
||||
};
|
||||
|
||||
struct context {
|
||||
u32 pc;
|
||||
var* localr;
|
||||
var* memr;
|
||||
var funcr;
|
||||
var upvalr;
|
||||
var* canary;
|
||||
var* stack;
|
||||
var* top;
|
||||
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 {
|
||||
@@ -219,11 +219,16 @@ struct nas_co {
|
||||
dead
|
||||
};
|
||||
|
||||
var stack[STACK_DEPTH];
|
||||
context ctx;
|
||||
status status;
|
||||
|
||||
nas_co() {clear();}
|
||||
nas_co() {
|
||||
ctx.stack = new var[STACK_DEPTH];
|
||||
clear();
|
||||
}
|
||||
~nas_co() {
|
||||
delete[] ctx.stack;
|
||||
}
|
||||
void clear();
|
||||
};
|
||||
|
||||
@@ -281,6 +286,10 @@ struct gc {
|
||||
/* main context temporary storage */
|
||||
context mctx;
|
||||
|
||||
/* global storage */
|
||||
var* main_context_global = nullptr;
|
||||
usize main_context_global_size = 0;
|
||||
|
||||
/* runtime context */
|
||||
context* rctx = nullptr;
|
||||
nas_co* cort = nullptr; // running coroutine
|
||||
@@ -315,7 +324,11 @@ struct gc {
|
||||
i64 max_mark_time = 0;
|
||||
i64 max_sweep_time = 0;
|
||||
|
||||
gc(context* _ctx): rctx(_ctx) {}
|
||||
void set(context* _ctx, var* _global, usize _size) {
|
||||
rctx = _ctx;
|
||||
main_context_global = _global;
|
||||
main_context_global_size = _size;
|
||||
}
|
||||
|
||||
private:
|
||||
/* gc functions */
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "nasal_import.h"
|
||||
#include "symbol_finder.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
linker::linker():
|
||||
show_path(false), lib_loaded(false),
|
||||
this_file(""), lib_path("") {
|
||||
@@ -346,3 +348,5 @@ const error& linker::link(
|
||||
delete old_tree_root;
|
||||
return err;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
class linker{
|
||||
namespace nasal {
|
||||
|
||||
class linker {
|
||||
private:
|
||||
bool show_path;
|
||||
bool lib_loaded;
|
||||
@@ -31,6 +33,7 @@ private:
|
||||
std::vector<std::string> module_load_stack;
|
||||
std::vector<std::string> envpath;
|
||||
|
||||
private:
|
||||
bool import_check(expr*);
|
||||
bool exist(const std::string&);
|
||||
u16 find(const std::string&);
|
||||
@@ -53,3 +56,5 @@ public:
|
||||
const auto& get_this_file() const {return this_file;}
|
||||
const auto& get_lib_path() const {return lib_path;}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -5,17 +5,20 @@
|
||||
#endif
|
||||
|
||||
#include "nasal_lexer.h"
|
||||
#include "repl.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
bool lexer::skip(char c) {
|
||||
return c==' ' || c=='\n' || c=='\t' || c=='\r' || c==0;
|
||||
}
|
||||
|
||||
bool lexer::is_id(char c) {
|
||||
return (c=='_') || ('a'<=c && c<='z') || ('A'<=c && c<='Z') || (c<0);
|
||||
return (c=='_') || std::isalpha(c) || (c<0);
|
||||
}
|
||||
|
||||
bool lexer::is_hex(char c) {
|
||||
return ('0'<=c && c<='9') || ('a'<=c && c<='f') || ('A'<=c && c<='F');
|
||||
return std::isxdigit(c);
|
||||
}
|
||||
|
||||
bool lexer::is_oct(char c) {
|
||||
@@ -23,7 +26,7 @@ bool lexer::is_oct(char c) {
|
||||
}
|
||||
|
||||
bool lexer::is_dec(char c) {
|
||||
return '0'<=c && c<='9';
|
||||
return std::isdigit(c);
|
||||
}
|
||||
|
||||
bool lexer::is_str(char c) {
|
||||
@@ -58,10 +61,18 @@ void lexer::err_char() {
|
||||
err.err("lexer",
|
||||
{line, column-1, line, column, filename},
|
||||
"invalid character 0x"+chrhex(c));
|
||||
err.fatal("lexer", "fatal error occurred, stop");
|
||||
++invalid_char;
|
||||
}
|
||||
|
||||
void lexer::open(const std::string& file) {
|
||||
if (repl::info::instance()->in_repl_mode &&
|
||||
repl::info::instance()->repl_file_name==file) {
|
||||
err.load(file);
|
||||
filename = file;
|
||||
res = repl::info::instance()->repl_file_source;
|
||||
return;
|
||||
}
|
||||
|
||||
// check file exsits and it is a regular file
|
||||
struct stat buffer;
|
||||
if (stat(file.c_str(), &buffer)==0 && !S_ISREG(buffer.st_mode)) {
|
||||
@@ -115,7 +126,7 @@ std::string lexer::utf8_gen() {
|
||||
err.err("lexer",
|
||||
{line, column-1, line, column, filename},
|
||||
"invalid utf-8 <"+utf_info+">");
|
||||
err.fatal("lexer", "fatal error occurred, stop");
|
||||
++invalid_char;
|
||||
}
|
||||
str += tmp;
|
||||
column += 2; // may have some problems because not all the unicode takes 2 space
|
||||
@@ -127,7 +138,7 @@ token lexer::id_gen() {
|
||||
u32 begin_line = line;
|
||||
u32 begin_column = column;
|
||||
std::string str = "";
|
||||
while(ptr<res.size() && (is_id(res[ptr])||is_dec(res[ptr]))) {
|
||||
while(ptr<res.size() && (is_id(res[ptr]) || is_dec(res[ptr]))) {
|
||||
if (res[ptr]<0) { // utf-8
|
||||
str += utf8_gen();
|
||||
} else { // ascii
|
||||
@@ -350,8 +361,18 @@ const error& lexer::scan(const std::string& file) {
|
||||
} else {
|
||||
err_char();
|
||||
}
|
||||
if (invalid_char>10) {
|
||||
err.err("lexer", "too many invalid characters, stop");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (toks.size()) {
|
||||
toks.push_back({toks.back().loc, tok::eof, "<eof>"});
|
||||
} else {
|
||||
toks.push_back({{line, column, line, column, filename}, tok::eof, "<eof>"});
|
||||
}
|
||||
toks.push_back({{line, column, line, column, filename}, tok::eof, "<eof>"});
|
||||
res = "";
|
||||
return err;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#define S_ISREG(m) (((m)&0xF000)==0x8000)
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
enum class tok:u32 {
|
||||
null=0, // null token (default token type)
|
||||
num, // number literal
|
||||
@@ -95,8 +97,11 @@ private:
|
||||
usize ptr;
|
||||
std::string filename;
|
||||
std::string res;
|
||||
|
||||
error err;
|
||||
u64 invalid_char;
|
||||
std::vector<token> toks;
|
||||
|
||||
const std::unordered_map<std::string, tok> typetbl {
|
||||
{"true" ,tok::tktrue },
|
||||
{"false" ,tok::tkfalse },
|
||||
@@ -175,7 +180,9 @@ private:
|
||||
token dots();
|
||||
token calc_opr();
|
||||
public:
|
||||
lexer(): line(1), column(0), ptr(0), filename(""), res("") {}
|
||||
lexer(): line(1), column(0), ptr(0), filename(""), res(""), invalid_char(0) {}
|
||||
const error& scan(const std::string&);
|
||||
const std::vector<token>& result() const {return toks;}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
enum op_code_type:u8 {
|
||||
op_exit, // stop the virtual machine
|
||||
op_intg, // global scope size, set stack top
|
||||
op_intg, // init global scope
|
||||
op_intl, // local scope size
|
||||
op_loadg, // load global value
|
||||
op_loadl, // load local value
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "nasal_ast.h"
|
||||
#include "nasal_parse.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
const error& parse::compile(const lexer& lexer) {
|
||||
toks=lexer.result().data();
|
||||
ptr=in_func=in_loop=0;
|
||||
@@ -13,7 +15,7 @@ const error& parse::compile(const lexer& lexer) {
|
||||
match(tok::semi);
|
||||
} else if (need_semi_check(root->get_expressions().back()) && !lookahead(tok::eof)) {
|
||||
// the last expression can be recognized without semi
|
||||
die(prevspan, "expected \";\"");
|
||||
die(prevspan, "expected \";\" after this token");
|
||||
}
|
||||
}
|
||||
update_location(root);
|
||||
@@ -60,6 +62,13 @@ void parse::die(const span& loc, std::string info) {
|
||||
err.err("parse", loc, info);
|
||||
}
|
||||
|
||||
void parse::next() {
|
||||
if (lookahead(tok::eof)) {
|
||||
return;
|
||||
}
|
||||
++ptr;
|
||||
}
|
||||
|
||||
void parse::match(tok type, const char* info) {
|
||||
if (!lookahead(type)) {
|
||||
if (info) {
|
||||
@@ -70,13 +79,10 @@ void parse::match(tok type, const char* info) {
|
||||
case tok::num:die(thisspan, "expected number"); break;
|
||||
case tok::str:die(thisspan, "expected string"); break;
|
||||
case tok::id: die(thisspan, "expected identifier");break;
|
||||
default: die(thisspan, "expected '"+tokname.at(type)+"'"); break;
|
||||
default: die(thisspan, "expected \""+tokname.at(type)+"\""); break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (lookahead(tok::eof)) {
|
||||
return;
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
@@ -91,7 +97,7 @@ bool parse::is_call(tok type) {
|
||||
bool parse::check_comma(const tok* panic_set) {
|
||||
for(u32 i=0;panic_set[i]!=tok::null;++i) {
|
||||
if (lookahead(panic_set[i])) {
|
||||
die(prevspan, "expected ',' between scalars");
|
||||
die(prevspan, "expected \",\" between scalars");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -118,7 +124,11 @@ bool parse::check_tuple() {
|
||||
}
|
||||
|
||||
bool parse::check_func_end(expr* node) {
|
||||
auto type=node->get_type();
|
||||
// avoid error parse caused nullptr return value
|
||||
if (!node) {
|
||||
return true;
|
||||
}
|
||||
auto type = node->get_type();
|
||||
if (type==expr_type::ast_func) {
|
||||
return true;
|
||||
} else if (type==expr_type::ast_def) {
|
||||
@@ -129,9 +139,21 @@ bool parse::check_func_end(expr* node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool parse::check_in_curve_multi_definition() {
|
||||
// we do not allow syntax like:
|
||||
// func {}(var a, b, c)
|
||||
// but we still allow syntax like:
|
||||
// func {}(var a = 1)
|
||||
// in fact, this syntax is not recommended
|
||||
if (!lookahead(tok::lcurve) || toks[ptr+1].type!=tok::var) {
|
||||
return false;
|
||||
}
|
||||
return toks[ptr+2].type==tok::id && toks[ptr+3].type==tok::comma;
|
||||
}
|
||||
|
||||
bool parse::check_special_call() {
|
||||
// special call means like this: function_name(a:1,b:2,c:3);
|
||||
u32 check_ptr=ptr, curve=1, bracket=0, brace=0;
|
||||
u32 check_ptr = ptr, curve = 1, bracket = 0, brace = 0;
|
||||
while(toks[++check_ptr].type!=tok::eof && curve) {
|
||||
switch(toks[check_ptr].type) {
|
||||
case tok::lcurve: ++curve; break;
|
||||
@@ -154,7 +176,11 @@ bool parse::check_special_call() {
|
||||
}
|
||||
|
||||
bool parse::need_semi_check(expr* node) {
|
||||
auto type=node->get_type();
|
||||
// avoid error parse caused nullptr return value
|
||||
if (!node) {
|
||||
return true;
|
||||
}
|
||||
auto type = node->get_type();
|
||||
if (type==expr_type::ast_for ||
|
||||
type==expr_type::ast_forei ||
|
||||
type==expr_type::ast_while ||
|
||||
@@ -165,7 +191,10 @@ bool parse::need_semi_check(expr* node) {
|
||||
}
|
||||
|
||||
void parse::update_location(expr* node) {
|
||||
node->update_location(toks[ptr].loc);
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
node->update_location(toks[ptr-1].loc);
|
||||
}
|
||||
|
||||
null_expr* parse::null() {
|
||||
@@ -228,7 +257,7 @@ vector_expr* parse::vec() {
|
||||
}
|
||||
}
|
||||
update_location(node);
|
||||
match(tok::rbracket, "expected ']' when generating vector");
|
||||
match(tok::rbracket, "expected \"]\" when generating vector");
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -240,13 +269,13 @@ hash_expr* parse::hash() {
|
||||
if (lookahead(tok::comma)) {
|
||||
match(tok::comma);
|
||||
} else if (lookahead(tok::id) || lookahead(tok::str)) { // first set of hashmember
|
||||
die(prevspan, "expected ',' between hash members");
|
||||
die(prevspan, "expected \",\" between hash members");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
update_location(node);
|
||||
match(tok::rbrace, "expected '}' when generating hash");
|
||||
match(tok::rbrace, "expected \"}\" when generating hash");
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -301,13 +330,13 @@ void parse::params(function* func_node) {
|
||||
if (lookahead(tok::comma)) {
|
||||
match(tok::comma);
|
||||
} else if (lookahead(tok::id)) { // first set of identifier
|
||||
die(prevspan, "expected ',' between identifiers");
|
||||
die(prevspan, "expected \",\" between identifiers");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
update_location(func_node);
|
||||
match(tok::rcurve, "expected ')' after parameter list");
|
||||
match(tok::rcurve, "expected \")\" after parameter list");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -348,7 +377,7 @@ expr* parse::expression() {
|
||||
case tok::cont: return continue_expression();
|
||||
case tok::brk: return break_expression();
|
||||
case tok::ret: return return_expression();
|
||||
case tok::semi: break;
|
||||
case tok::semi: break;
|
||||
default:
|
||||
die(thisspan, "incorrect token <"+toks[ptr].str+">");
|
||||
next();
|
||||
@@ -373,10 +402,10 @@ code_block* parse::expression_block() {
|
||||
match(tok::semi);
|
||||
} else if (need_semi_check(node->get_expressions().back()) && !lookahead(tok::rbrace)) {
|
||||
// the last expression can be recognized without semi
|
||||
die(prevspan, "expected ';'");
|
||||
die(prevspan, "expected \";\" after this token");
|
||||
}
|
||||
}
|
||||
match(tok::rbrace, "expected '}' when generating expressions");
|
||||
match(tok::rbrace, "expected \"}\" when generating expressions");
|
||||
} else {
|
||||
node->add_expression(expression());
|
||||
if (lookahead(tok::semi)) {
|
||||
@@ -626,8 +655,12 @@ expr* parse::scalar() {
|
||||
die(thisspan, "expected scalar");
|
||||
return null();
|
||||
}
|
||||
// check call and avoid ambiguous syntax
|
||||
if (is_call(toks[ptr].type) && !(lookahead(tok::lcurve) && toks[ptr+1].type==tok::var)) {
|
||||
// check call and avoid ambiguous syntax:
|
||||
// var f = func(){}
|
||||
// (var a, b, c) = (1, 2, 3);
|
||||
// will be incorrectly recognized like:
|
||||
// var f = func(){}(var a, b, c)
|
||||
if (is_call(toks[ptr].type) && !check_in_curve_multi_definition()) {
|
||||
auto call_node = new call_expr(toks[ptr].loc);
|
||||
call_node->set_first(node);
|
||||
while(is_call(toks[ptr].type)) {
|
||||
@@ -685,7 +718,7 @@ call_vector* parse::callv() {
|
||||
die(thisspan, "expected index value");
|
||||
}
|
||||
update_location(node);
|
||||
match(tok::rbracket, "expected ']' when calling vector");
|
||||
match(tok::rbracket, "expected \"]\" when calling vector");
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -712,7 +745,7 @@ call_function* parse::callf() {
|
||||
break;
|
||||
}
|
||||
update_location(node);
|
||||
match(tok::rcurve, "expected ')' when calling function");
|
||||
match(tok::rcurve, "expected \")\" when calling function");
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -780,7 +813,7 @@ multi_identifier* parse::multi_id() {
|
||||
if (lookahead(tok::comma)) {
|
||||
match(tok::comma);
|
||||
} else if (lookahead(tok::id)) { // first set of identifier
|
||||
die(prevspan, "expected ',' between identifiers");
|
||||
die(prevspan, "expected \",\" between identifiers");
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -810,7 +843,7 @@ tuple_expr* parse::multi_scalar() {
|
||||
}
|
||||
}
|
||||
update_location(node);
|
||||
match(tok::rcurve, "expected ')' after multi-scalar");
|
||||
match(tok::rcurve, "expected \")\" after multi-scalar");
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -877,7 +910,7 @@ for_expr* parse::for_loop() {
|
||||
} else {
|
||||
node->set_initial(calc());
|
||||
}
|
||||
match(tok::semi, "expected ';' in for(;;)");
|
||||
match(tok::semi, "expected \";\" in for(;;)");
|
||||
|
||||
// conditional expression
|
||||
if (lookahead(tok::eof)) {
|
||||
@@ -888,7 +921,7 @@ for_expr* parse::for_loop() {
|
||||
} else {
|
||||
node->set_condition(calc());
|
||||
}
|
||||
match(tok::semi, "expected ';' in for(;;)");
|
||||
match(tok::semi, "expected \";\" in for(;;)");
|
||||
|
||||
//after loop expression
|
||||
if (lookahead(tok::eof)) {
|
||||
@@ -925,7 +958,7 @@ forei_expr* parse::forei_loop() {
|
||||
die(thisspan, "expected iterator");
|
||||
}
|
||||
node->set_iterator(iter_gen());
|
||||
match(tok::semi, "expected ';' in foreach/forindex(iter;vector)");
|
||||
match(tok::semi, "expected \";\" in foreach/forindex(iter;vector)");
|
||||
if (lookahead(tok::eof)) {
|
||||
die(thisspan, "expected vector");
|
||||
}
|
||||
@@ -1026,3 +1059,5 @@ return_expr* parse::return_expression() {
|
||||
update_location(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
#include "nasal_lexer.h"
|
||||
#include "nasal_err.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class parse {
|
||||
|
||||
#define thisspan (toks[ptr].loc)
|
||||
@@ -77,13 +79,14 @@ private:
|
||||
|
||||
private:
|
||||
void die(const span&,std::string);
|
||||
void next() {++ptr;}
|
||||
void match(tok type, const char* info=nullptr);
|
||||
void next();
|
||||
void match(tok, const char* info=nullptr);
|
||||
bool lookahead(tok);
|
||||
bool is_call(tok);
|
||||
bool check_comma(const tok*);
|
||||
bool check_tuple();
|
||||
bool check_func_end(expr*);
|
||||
bool check_in_curve_multi_definition();
|
||||
bool check_special_call();
|
||||
bool need_semi_check(expr*);
|
||||
void update_location(expr*);
|
||||
@@ -154,4 +157,6 @@ public:
|
||||
}
|
||||
const error& compile(const lexer&);
|
||||
static void easter_egg();
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#include "nasal_vm.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void vm::init(
|
||||
const std::vector<std::string>& strs,
|
||||
const std::vector<f64>& nums,
|
||||
const std::vector<nasal_builtin_table>& natives,
|
||||
const std::vector<opcode>& code,
|
||||
const std::unordered_map<std::string, i32>& global,
|
||||
const std::unordered_map<std::string, i32>& global_symbol,
|
||||
const std::vector<std::string>& filenames,
|
||||
const std::vector<std::string>& argv
|
||||
) {
|
||||
@@ -13,6 +15,7 @@ void vm::init(
|
||||
cstr = strs.data();
|
||||
bytecode = code.data();
|
||||
files = filenames.data();
|
||||
global_size = global_symbol.size();
|
||||
|
||||
/* set native functions */
|
||||
native = natives;
|
||||
@@ -23,28 +26,29 @@ void vm::init(
|
||||
ctx.memr = nullptr;
|
||||
ctx.funcr = nil;
|
||||
ctx.upvalr = nil;
|
||||
ctx.canary = stack+STACK_DEPTH-1; // stack[STACK_DEPTH-1]
|
||||
ctx.top = stack;
|
||||
ctx.stack = stack;
|
||||
ctx.canary = ctx.stack+STACK_DEPTH-1; // stack[STACK_DEPTH-1]
|
||||
ctx.top = ctx.stack; // nothing is on stack
|
||||
|
||||
/* clear main stack */
|
||||
/* clear main stack and global */
|
||||
for(u32 i = 0; i<STACK_DEPTH; ++i) {
|
||||
stack[i] = nil;
|
||||
ctx.stack[i] = nil;
|
||||
global[i] = nil;
|
||||
}
|
||||
|
||||
/* init gc */
|
||||
ngc.set(&ctx, global, global_size);
|
||||
ngc.init(strs, argv);
|
||||
|
||||
/* init vm globals */
|
||||
auto map_instance = ngc.alloc(vm_map);
|
||||
stack[global.at("globals")] = map_instance;
|
||||
for(const auto& i : global) {
|
||||
map_instance.map().mapper[i.first] = stack+i.second;
|
||||
global[global_symbol.at("globals")] = map_instance;
|
||||
for(const auto& i : global_symbol) {
|
||||
map_instance.map().mapper[i.first] = global+i.second;
|
||||
}
|
||||
|
||||
/* init vm arg */
|
||||
auto arg_instance = ngc.alloc(vm_vec);
|
||||
stack[global.at("arg")] = arg_instance;
|
||||
global[global_symbol.at("arg")] = arg_instance;
|
||||
arg_instance.vec().elems = ngc.env_argv;
|
||||
}
|
||||
|
||||
@@ -89,18 +93,19 @@ void vm::valinfo(var& val) {
|
||||
}
|
||||
|
||||
void vm::traceback() {
|
||||
/* bytecode[0].num is the global size */
|
||||
var* bottom = ngc.rctx->stack==stack? stack+bytecode[0].num:ngc.rctx->stack;
|
||||
var* ctx_top = ngc.rctx->stack==stack? ctx.top:ngc.rctx->top;
|
||||
var* bottom = ctx.stack;
|
||||
var* top = ctx.top;
|
||||
|
||||
std::stack<u32> ret;
|
||||
for(var* i = bottom; i<=ctx_top; ++i) {
|
||||
for(var* i = bottom; i<=top; ++i) {
|
||||
if (i->type==vm_ret && i->ret()!=0) {
|
||||
ret.push(i->ret());
|
||||
}
|
||||
}
|
||||
ret.push(ctx.pc); // store the position program crashed
|
||||
|
||||
std::clog << "trace back ("
|
||||
<< (ngc.rctx->stack==stack? "main":"coroutine")
|
||||
<< (ngc.cort? "coroutine":"main")
|
||||
<< ")\n";
|
||||
codestream::set(cnum, cstr, native.data(), files);
|
||||
for(u32 p = 0, same = 0, prev = 0xffffffff; !ret.empty(); prev = p, ret.pop()) {
|
||||
@@ -121,19 +126,17 @@ void vm::traceback() {
|
||||
}
|
||||
|
||||
void vm::stackinfo(const u32 limit = 10) {
|
||||
/* bytecode[0].num is the global size */
|
||||
const u32 gsize = ngc.rctx->stack==stack? bytecode[0].num:0;
|
||||
var* t = ctx.top;
|
||||
var* bottom = ngc.rctx->stack+gsize;
|
||||
var* top = ctx.top;
|
||||
var* bottom = ctx.stack;
|
||||
std::clog << "stack (0x" << std::hex << (u64)bottom << std::dec;
|
||||
std::clog << " <+" << gsize << ">, limit " << limit << ", total ";
|
||||
std::clog << (t<bottom? 0:(i64)(t-bottom+1)) << ")\n";
|
||||
for(u32 i = 0; i<limit && t>=bottom; ++i, --t) {
|
||||
std::clog << ", limit " << limit << ", total ";
|
||||
std::clog << (top<bottom? 0:(i64)(top-bottom+1)) << ")\n";
|
||||
for(u32 i = 0; i<limit && top>=bottom; ++i, --top) {
|
||||
std::clog << " 0x" << std::hex
|
||||
<< std::setw(6) << std::setfill('0')
|
||||
<< (u64)(t-ngc.rctx->stack) << std::dec
|
||||
<< (u64)(top-bottom) << std::dec
|
||||
<< " ";
|
||||
valinfo(t[0]);
|
||||
valinfo(top[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +144,7 @@ void vm::reginfo() {
|
||||
std::clog << "registers (" << (ngc.cort? "coroutine":"main")
|
||||
<< ")\n" << std::hex
|
||||
<< " [pc ] | pc | 0x" << ctx.pc << "\n"
|
||||
<< " [global] | addr | 0x" << (u64)stack << "\n"
|
||||
<< " [global] | addr | 0x" << (u64)global << "\n"
|
||||
<< " [local ] | addr | 0x" << (u64)ctx.localr << "\n"
|
||||
<< " [memr ] | addr | 0x" << (u64)ctx.memr << "\n"
|
||||
<< " [canary] | addr | 0x" << (u64)ctx.canary << "\n"
|
||||
@@ -152,17 +155,16 @@ void vm::reginfo() {
|
||||
}
|
||||
|
||||
void vm::gstate() {
|
||||
// bytecode[0].op is op_intg
|
||||
if (!bytecode[0].num || stack[0].type==vm_none) {
|
||||
if (!global_size || global[0].type==vm_none) {
|
||||
return;
|
||||
}
|
||||
std::clog << "global (0x" << std::hex
|
||||
<< (u64)stack << " <+0>)\n" << std::dec;
|
||||
for(u32 i = 0; i<bytecode[0].num; ++i) {
|
||||
<< (u64)global << ")\n" << std::dec;
|
||||
for(usize i = 0; i<global_size; ++i) {
|
||||
std::clog << " 0x" << std::hex << std::setw(6)
|
||||
<< std::setfill('0') << i << std::dec
|
||||
<< " ";
|
||||
valinfo(stack[i]);
|
||||
valinfo(global[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +174,7 @@ void vm::lstate() {
|
||||
}
|
||||
const u32 lsize = ctx.funcr.func().lsize;
|
||||
std::clog << "local (0x" << std::hex << (u64)ctx.localr
|
||||
<< " <+" << (u64)(ctx.localr-ngc.rctx->stack)
|
||||
<< " <+" << (u64)(ctx.localr-ctx.stack)
|
||||
<< ">)\n" << std::dec;
|
||||
for(u32 i = 0; i<lsize; ++i) {
|
||||
std::clog << " 0x" << std::hex << std::setw(6)
|
||||
@@ -217,7 +219,7 @@ void vm::die(const std::string& str) {
|
||||
detail();
|
||||
}
|
||||
|
||||
if (ngc.rctx->stack==stack) {
|
||||
if (!ngc.cort) {
|
||||
// in main context, exit directly
|
||||
std::exit(1);
|
||||
} else {
|
||||
@@ -442,3 +444,5 @@ mcallh: exec_nodie(o_mcallh); // -0
|
||||
ret: exec_nodie(o_ret ); // -2
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,10 +13,12 @@
|
||||
#pragma warning (disable:4102)
|
||||
#endif
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class vm {
|
||||
protected:
|
||||
|
||||
/* registers and constants of vm */
|
||||
/* registers of vm */
|
||||
context ctx;
|
||||
|
||||
/* constants */
|
||||
@@ -29,7 +31,8 @@ protected:
|
||||
gc ngc;
|
||||
|
||||
/* main stack */
|
||||
var stack[STACK_DEPTH];
|
||||
var* global = nullptr;
|
||||
usize global_size = 0;
|
||||
|
||||
/* values used for debugger */
|
||||
const std::string* files = nullptr; // file name list
|
||||
@@ -46,7 +49,7 @@ protected:
|
||||
const std::vector<std::string>&);
|
||||
|
||||
/* debug functions */
|
||||
bool verbose;
|
||||
bool verbose = false;
|
||||
void valinfo(var&);
|
||||
void traceback();
|
||||
void stackinfo(const u32);
|
||||
@@ -151,7 +154,14 @@ protected:
|
||||
public:
|
||||
|
||||
/* constructor of vm instance */
|
||||
vm(): ngc(&ctx), verbose(false) {}
|
||||
vm() {
|
||||
ctx.stack = new var[STACK_DEPTH];
|
||||
global = new var[STACK_DEPTH];
|
||||
}
|
||||
~vm() {
|
||||
delete[] ctx.stack;
|
||||
delete[] global;
|
||||
}
|
||||
|
||||
/* execution entry */
|
||||
void run(
|
||||
@@ -173,7 +183,7 @@ inline bool vm::cond(var& val) {
|
||||
|
||||
inline void vm::o_intg() {
|
||||
// global values store on stack
|
||||
ctx.top += imm[ctx.pc];
|
||||
// ctx.top += imm[ctx.pc];
|
||||
// point to the top
|
||||
--ctx.top;
|
||||
}
|
||||
@@ -184,7 +194,7 @@ inline void vm::o_intl() {
|
||||
}
|
||||
|
||||
inline void vm::o_loadg() {
|
||||
stack[imm[ctx.pc]] = (ctx.top--)[0];
|
||||
global[imm[ctx.pc]] = (ctx.top--)[0];
|
||||
}
|
||||
|
||||
inline void vm::o_loadl() {
|
||||
@@ -534,7 +544,7 @@ inline void vm::o_feach() {
|
||||
|
||||
inline void vm::o_callg() {
|
||||
// get main stack directly
|
||||
(++ctx.top)[0] = stack[imm[ctx.pc]];
|
||||
(++ctx.top)[0] = global[imm[ctx.pc]];
|
||||
}
|
||||
|
||||
inline void vm::o_calll() {
|
||||
@@ -819,7 +829,7 @@ inline void vm::o_slc2() {
|
||||
}
|
||||
|
||||
inline void vm::o_mcallg() {
|
||||
ctx.memr = stack+imm[ctx.pc];
|
||||
ctx.memr = global+imm[ctx.pc];
|
||||
(++ctx.top)[0] = ctx.memr[0];
|
||||
// push value in this memory space on stack
|
||||
// to avoid being garbage collected
|
||||
@@ -946,4 +956,6 @@ inline void vm::o_ret() {
|
||||
if (!ctx.pc) {
|
||||
ngc.ctxreserve();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "optimizer.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
void optimizer::const_string(
|
||||
binary_operator* node,
|
||||
string_literal* left_node,
|
||||
@@ -129,4 +131,6 @@ bool optimizer::visit_unary_operator(unary_operator* node) {
|
||||
|
||||
void optimizer::do_optimization(code_block* root) {
|
||||
root->accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "nasal_ast.h"
|
||||
#include "ast_visitor.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class optimizer:public ast_visitor {
|
||||
private:
|
||||
void const_string(binary_operator*, string_literal*, string_literal*);
|
||||
@@ -18,3 +20,5 @@ public:
|
||||
public:
|
||||
void do_optimization(code_block*);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
147
src/repl.cpp
Normal file
147
src/repl.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
#include "repl.h"
|
||||
#include "nasal_lexer.h"
|
||||
#include "nasal_parse.h"
|
||||
#include "nasal_import.h"
|
||||
#include "optimizer.h"
|
||||
#include "nasal_codegen.h"
|
||||
#include "nasal_vm.h"
|
||||
|
||||
namespace nasal {
|
||||
namespace repl {
|
||||
|
||||
std::string repl::readline(std::string prompt = ">>> ") {
|
||||
auto line = std::string("");
|
||||
std::cout << prompt;
|
||||
std::getline(std::cin, line,'\n');
|
||||
return line;
|
||||
}
|
||||
|
||||
void repl::update_temp_file() {
|
||||
auto content = std::string("");
|
||||
for(const auto& i : source) {
|
||||
content += i + "\n";
|
||||
}
|
||||
info::instance()->repl_file_source = content;
|
||||
}
|
||||
|
||||
bool repl::check_need_more_input() {
|
||||
while(true) {
|
||||
update_temp_file();
|
||||
auto nasal_lexer = std::unique_ptr<lexer>(new lexer);
|
||||
if (nasal_lexer->scan("<nasal-repl>").geterr()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
i64 in_curve = 0;
|
||||
i64 in_bracket = 0;
|
||||
i64 in_brace = 0;
|
||||
for(const auto& t : nasal_lexer->result()) {
|
||||
switch(t.type) {
|
||||
case tok::lcurve: ++in_curve; break;
|
||||
case tok::rcurve: --in_curve; break;
|
||||
case tok::lbracket: ++in_bracket; break;
|
||||
case tok::rbracket: --in_bracket; break;
|
||||
case tok::lbrace: ++in_brace; break;
|
||||
case tok::rbrace: --in_brace; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (in_curve<=0 && in_bracket<=0 && in_brace<=0) {
|
||||
break;
|
||||
}
|
||||
auto line = readline("... ");
|
||||
source.back() += "\n" + line;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void repl::help() {
|
||||
std::cout << ".h, .help | show help\n";
|
||||
std::cout << ".e, .exit | quit the REPL\n";
|
||||
std::cout << ".q, .quit | quit the REPL\n";
|
||||
std::cout << ".c, .clear | clear the screen\n";
|
||||
std::cout << "\n";
|
||||
}
|
||||
|
||||
bool repl::run() {
|
||||
update_temp_file();
|
||||
|
||||
using clk = std::chrono::high_resolution_clock;
|
||||
const auto den = clk::duration::period::den;
|
||||
auto start = clk::now();
|
||||
|
||||
auto nasal_lexer = std::unique_ptr<lexer>(new lexer);
|
||||
auto nasal_parser = std::unique_ptr<parse>(new parse);
|
||||
auto nasal_linker = std::unique_ptr<linker>(new linker);
|
||||
auto nasal_opt = std::unique_ptr<optimizer>(new optimizer);
|
||||
auto nasal_codegen = std::unique_ptr<codegen>(new codegen);
|
||||
auto nasal_runtime = std::unique_ptr<vm>(new vm);
|
||||
|
||||
if (nasal_lexer->scan("<nasal-repl>").geterr()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nasal_parser->compile(*nasal_lexer).geterr()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nasal_linker->link(*nasal_parser, "<nasal-repl>", true).geterr()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nasal_opt->do_optimization(nasal_parser->tree());
|
||||
if (nasal_codegen->compile(*nasal_parser, *nasal_linker).geterr()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto end = clk::now();
|
||||
std::clog << "[compile time: " << (end-start).count()*1000.0/den << " ms]\n";
|
||||
nasal_runtime->run(*nasal_codegen, *nasal_linker, {}, false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void repl::execute() {
|
||||
source = {};
|
||||
info::instance()->in_repl_mode = true;
|
||||
|
||||
std::cout << "Nasal REPL interpreter(experimental).\n";
|
||||
help();
|
||||
|
||||
while(true) {
|
||||
auto line = readline();
|
||||
if (!line.length()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line == ".e" || line == ".exit" || line == ".q" || line == ".quit") {
|
||||
break;
|
||||
} else if (line == ".h" || line == ".help") {
|
||||
help();
|
||||
continue;
|
||||
} else if (line == ".c" || line == ".clear") {
|
||||
std::cout << "\033c";
|
||||
continue;
|
||||
} else if (line[0] == "."[0]) {
|
||||
std::cout << "no such command \"" << line;
|
||||
std::cout << "\", input \".help\" for help\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
source.push_back(line);
|
||||
if (!check_need_more_input()) {
|
||||
source.pop_back();
|
||||
continue;
|
||||
}
|
||||
|
||||
// run program
|
||||
if (!run()) {
|
||||
source.pop_back();
|
||||
}
|
||||
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
42
src/repl.h
Normal file
42
src/repl.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "nasal.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
|
||||
namespace nasal {
|
||||
namespace repl {
|
||||
|
||||
struct info {
|
||||
bool in_repl_mode = false;
|
||||
std::string repl_file_name = "<nasal-repl>";
|
||||
std::string repl_file_source = "";
|
||||
|
||||
// singleton
|
||||
static info* instance() {
|
||||
static info info;
|
||||
return &info;
|
||||
}
|
||||
};
|
||||
|
||||
class repl {
|
||||
private:
|
||||
std::vector<std::string> source;
|
||||
|
||||
private:
|
||||
std::string readline(std::string);
|
||||
bool check_need_more_input();
|
||||
void update_temp_file();
|
||||
void help();
|
||||
bool run();
|
||||
|
||||
public:
|
||||
void execute();
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
#include "symbol_finder.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
bool symbol_finder::visit_definition_expr(definition_expr* node) {
|
||||
if (node->get_variable_name()) {
|
||||
symbols.push_back({
|
||||
node->get_variable_name()->get_name(),
|
||||
node->get_variable_name()->get_location().file
|
||||
node->get_variable_name()->get_location()
|
||||
});
|
||||
} else {
|
||||
for(auto i : node->get_variables()->get_variables()) {
|
||||
symbols.push_back({
|
||||
i->get_name(),
|
||||
i->get_location().file
|
||||
i->get_location()
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -30,7 +32,8 @@ bool symbol_finder::visit_iter_expr(iter_expr* node) {
|
||||
if (node->get_name()) {
|
||||
symbols.push_back({
|
||||
node->get_name()->get_name(),
|
||||
node->get_name()->get_location().file});
|
||||
node->get_name()->get_location()
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -39,4 +42,6 @@ const std::vector<symbol_finder::symbol_info>& symbol_finder::do_find(code_block
|
||||
symbols.clear();
|
||||
root->accept(this);
|
||||
return symbols;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,11 +7,13 @@
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace nasal {
|
||||
|
||||
class symbol_finder:public ast_visitor {
|
||||
public:
|
||||
struct symbol_info {
|
||||
std::string name;
|
||||
std::string file;
|
||||
span location;
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -22,4 +24,6 @@ public:
|
||||
bool visit_function(function*) override;
|
||||
bool visit_iter_expr(iter_expr*) override;
|
||||
const std::vector<symbol_finder::symbol_info>& do_find(code_block*);
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
12
std/csv.nas
12
std/csv.nas
@@ -1,16 +1,16 @@
|
||||
# lib csv.nas
|
||||
# ValKmjolnir 2022/10/15
|
||||
var read_csv=func(path,delimeter=",",endline="\n"){
|
||||
var context=io.readfile(path);
|
||||
context=split(endline,context);
|
||||
var read = func(path, delimeter=",", endline="\n"){
|
||||
var context = io.readfile(path);
|
||||
context = split(endline, context);
|
||||
forindex(var i;context){
|
||||
context[i]=split(delimeter,context[i]);
|
||||
context[i] = split(delimeter,context[i]);
|
||||
}
|
||||
if(size(context)<=1){
|
||||
die("incorrect csv file <"~path~">: "~size(context)~" line(s).");
|
||||
}
|
||||
return {
|
||||
property:context[0],
|
||||
data:context[1:]
|
||||
property: context[0],
|
||||
data: context[1:]
|
||||
};
|
||||
}
|
||||
109
std/file.nas
109
std/file.nas
@@ -1,78 +1,85 @@
|
||||
# lib file.nas
|
||||
# ValKmjolnir 2022/3/6
|
||||
var file={
|
||||
SEEK_SET:io.SEEK_SET,
|
||||
SEEK_CUR:io.SEEK_CUR,
|
||||
SEEK_END:io.SEEK_END,
|
||||
new: func(filename,mode="r"){
|
||||
if(!io.exists(filename))
|
||||
return nil;
|
||||
var fd=io.open(filename,mode);
|
||||
return {
|
||||
close: func(){io.close(fd);},
|
||||
read: func(len){
|
||||
var buf=mut("");
|
||||
io.read(fd,buf,len);
|
||||
return buf;
|
||||
},
|
||||
write: func(str){return io.write(fd,str);},
|
||||
seek: func(pos,whence){return io.seek(fd,pos,whence);},
|
||||
tell: func(){return io.tell(fd);},
|
||||
readln: func(){return io.readln(fd);},
|
||||
stat: func(){return io.stat(filename);},
|
||||
eof: func(){return io.eof(fd);}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
var find_all_files_with_extension=func(path,extensions...){
|
||||
var in_vec=func(ext){
|
||||
foreach(var i;extensions){
|
||||
if(ext==i){
|
||||
var SEEK_SET = io.SEEK_SET;
|
||||
|
||||
var SEEK_CUR = io.SEEK_CUR;
|
||||
|
||||
var SEEK_END = io.SEEK_END;
|
||||
|
||||
|
||||
var new = func(filename, mode="r"){
|
||||
if (!io.exists(filename)) {
|
||||
return nil;
|
||||
}
|
||||
var fd = io.open(filename, mode);
|
||||
return {
|
||||
close: func() {io.close(fd);},
|
||||
read: func(len) {
|
||||
var buf = mut("");
|
||||
io.read(fd, buf, len);
|
||||
return buf;
|
||||
},
|
||||
write: func(str) {return io.write(fd, str);},
|
||||
seek: func(pos, whence) {return io.seek(fd, pos, whence);},
|
||||
tell: func() {return io.tell(fd);},
|
||||
readln: func() {return io.readln(fd);},
|
||||
stat: func() {return io.stat(filename);},
|
||||
eof: func() {return io.eof(fd);}
|
||||
};
|
||||
}
|
||||
|
||||
var find_all_files_with_extension = func(path, extensions...){
|
||||
var in_vec = func(ext) {
|
||||
foreach(var i;extensions) {
|
||||
if (ext==i){
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
var res=[];
|
||||
var res = [];
|
||||
foreach(var f;find_all_files(path)){
|
||||
var tmp=split('.',f);
|
||||
if(size(tmp)>1 and in_vec(tmp[-1])){
|
||||
append(res,f);
|
||||
var tmp = split('.', f);
|
||||
if (size(tmp)>1 and in_vec(tmp[-1])) {
|
||||
append(res, f);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
var find_all_files=func(path){
|
||||
if(!io.exists(path))
|
||||
var find_all_files = func(path){
|
||||
if (!io.exists(path)) {
|
||||
return [];
|
||||
var dd=unix.opendir(path);
|
||||
var res=[];
|
||||
while(var n=unix.readdir(dd))
|
||||
if(unix.isfile(path~"/"~n))
|
||||
append(res,n);
|
||||
}
|
||||
var dd = unix.opendir(path);
|
||||
var res = [];
|
||||
while(var n = unix.readdir(dd))
|
||||
if(unix.isfile(path~"/"~n)) {
|
||||
append(res, n);
|
||||
}
|
||||
unix.closedir(dd);
|
||||
return res;
|
||||
}
|
||||
|
||||
var recursive_find_files=func(path){
|
||||
if(!io.exists(path))
|
||||
var recursive_find_files = func(path) {
|
||||
if (!io.exists(path)) {
|
||||
return nil;
|
||||
var dd=unix.opendir(path);
|
||||
var res={
|
||||
dir:path,
|
||||
files:[]
|
||||
}
|
||||
var dd = unix.opendir(path);
|
||||
var res = {
|
||||
dir: path,
|
||||
files: []
|
||||
};
|
||||
while(var n=unix.readdir(dd)){
|
||||
if(unix.isfile(path~"/"~n)){
|
||||
while(var n = unix.readdir(dd)) {
|
||||
if (unix.isfile(path~"/"~n)) {
|
||||
append(res.files,n);
|
||||
}elsif(unix.isdir(path~"/"~n) and n!="." and n!=".."){
|
||||
var tmp=recursive_find_files(path~"/"~n);
|
||||
if(tmp!=nil)
|
||||
} elsif (unix.isdir(path~"/"~n) and n!="." and n!="..") {
|
||||
var tmp = recursive_find_files(path~"/"~n);
|
||||
if (tmp!=nil) {
|
||||
append(res.files,tmp);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
unix.closedir(dd);
|
||||
return res;
|
||||
|
||||
331
std/json.nas
331
std/json.nas
@@ -1,59 +1,59 @@
|
||||
# lib json.nas
|
||||
# 2021 ValKmjolnir
|
||||
var JSON=func() {
|
||||
|
||||
var (
|
||||
j_eof,
|
||||
j_lbrace,
|
||||
j_rbrace,
|
||||
j_lbrkt,
|
||||
j_rbrkt,
|
||||
j_comma,
|
||||
j_colon,
|
||||
j_str,
|
||||
j_num,
|
||||
j_id
|
||||
)=(0,1,2,3,4,5,6,7,8,9);
|
||||
var j_content=[
|
||||
"eof",
|
||||
"`{`",
|
||||
"`}`",
|
||||
"`[`",
|
||||
"`]`",
|
||||
"`,`",
|
||||
"`:`",
|
||||
"string",
|
||||
"number",
|
||||
"identifier"
|
||||
];
|
||||
var (
|
||||
_j_eof, _j_lbrace, _j_rbrace, _j_lbrkt, _j_rbrkt,
|
||||
_j_comma, _j_colon, _j_str, _j_num, _j_id
|
||||
) = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
|
||||
|
||||
var text='';
|
||||
var line=1;
|
||||
var text_size=0;
|
||||
var ptr=0;
|
||||
var token={content:'',type:''};
|
||||
var _j_content = [
|
||||
"eof",
|
||||
"`{`",
|
||||
"`}`",
|
||||
"`[`",
|
||||
"`]`",
|
||||
"`,`",
|
||||
"`:`",
|
||||
"string",
|
||||
"number",
|
||||
"identifier"
|
||||
];
|
||||
|
||||
var init=func() {
|
||||
text='';
|
||||
line=1;
|
||||
text_size=0;
|
||||
ptr=0;
|
||||
token={content:'',type:''};
|
||||
var JSON = func() {
|
||||
|
||||
var text = "";
|
||||
var line = 1;
|
||||
var text_size = 0;
|
||||
var ptr = 0;
|
||||
var token = {
|
||||
content: "",
|
||||
type: ""
|
||||
};
|
||||
|
||||
var init = func() {
|
||||
text = "";
|
||||
line = 1;
|
||||
text_size = 0;
|
||||
ptr = 0;
|
||||
token = {
|
||||
content: "",
|
||||
type: ""
|
||||
};
|
||||
}
|
||||
|
||||
var isnum=func(c) {
|
||||
var isnum = func(c) {
|
||||
return '0'<=c and c<='9';
|
||||
}
|
||||
|
||||
var isid=func(c) {
|
||||
var tmp=c[0];
|
||||
var isid = func(c) {
|
||||
var tmp = c[0];
|
||||
return ('a'[0]<=tmp and tmp<='z'[0]) or
|
||||
('A'[0]<=tmp and tmp<='Z'[0]) or
|
||||
c=='_';
|
||||
('A'[0]<=tmp and tmp<='Z'[0]) or
|
||||
c=='_';
|
||||
}
|
||||
|
||||
var check=func() {
|
||||
var c=char(text[ptr]);
|
||||
var check = func() {
|
||||
var c = char(text[ptr]);
|
||||
return (
|
||||
c=='{' or c=='}' or
|
||||
c=='[' or c==']' or
|
||||
@@ -63,151 +63,152 @@ var JSON=func() {
|
||||
);
|
||||
}
|
||||
|
||||
var get=func(str) {
|
||||
var get = func(str) {
|
||||
init();
|
||||
if(!size(str)) {
|
||||
if (!size(str)) {
|
||||
println("JSON.parse: empty string");
|
||||
str="[]";
|
||||
str = "[]";
|
||||
}
|
||||
text=str;
|
||||
text_size=size(text);
|
||||
text = str;
|
||||
text_size = size(text);
|
||||
return;
|
||||
}
|
||||
|
||||
var next=func() {
|
||||
var next = func() {
|
||||
while(ptr<text_size and !check()) {
|
||||
if(char(text[ptr])=='\n')
|
||||
line+=1;
|
||||
ptr+=1;
|
||||
if (char(text[ptr])=='\n') {
|
||||
line += 1;
|
||||
}
|
||||
ptr += 1;
|
||||
}
|
||||
if(ptr>=text_size) {
|
||||
token.content="eof";
|
||||
token.type=j_eof;
|
||||
token.content = "eof";
|
||||
token.type = _j_eof;
|
||||
return;
|
||||
}
|
||||
|
||||
var c=char(text[ptr]);
|
||||
if(c=='{') {
|
||||
token.content='{';
|
||||
token.type=j_lbrace;
|
||||
} elsif(c=='}') {
|
||||
token.content='}';
|
||||
token.type=j_rbrace;
|
||||
} elsif(c=='[') {
|
||||
token.content='[';
|
||||
token.type=j_lbrkt;
|
||||
} elsif(c==']') {
|
||||
token.content=']';
|
||||
token.type=j_rbrkt;
|
||||
} elsif(c==',') {
|
||||
token.content=',';
|
||||
token.type=j_comma;
|
||||
} elsif(c==':') {
|
||||
token.content=':';
|
||||
token.type=j_colon;
|
||||
} elsif(c=='\"' or c=='\'') {
|
||||
var strbegin=c;
|
||||
var s="";
|
||||
ptr+=1;
|
||||
var c = char(text[ptr]);
|
||||
if (c=='{') {
|
||||
token.content = '{';
|
||||
token.type = _j_lbrace;
|
||||
} elsif (c=='}') {
|
||||
token.content = '}';
|
||||
token.type = _j_rbrace;
|
||||
} elsif (c=='[') {
|
||||
token.content = '[';
|
||||
token.type = _j_lbrkt;
|
||||
} elsif (c==']') {
|
||||
token.content = ']';
|
||||
token.type = _j_rbrkt;
|
||||
} elsif (c==',') {
|
||||
token.content = ',';
|
||||
token.type = _j_comma;
|
||||
} elsif (c==':') {
|
||||
token.content = ':';
|
||||
token.type = _j_colon;
|
||||
} elsif (c=='\"' or c=='\'') {
|
||||
var strbegin = c;
|
||||
var s = "";
|
||||
ptr += 1;
|
||||
while(ptr<text_size and char(text[ptr])!=strbegin) {
|
||||
s~=char(text[ptr]);
|
||||
ptr+=1;
|
||||
if(char(text[ptr-1])=="\\" and ptr<text_size) {
|
||||
s~=char(text[ptr]);
|
||||
ptr+=1;
|
||||
s ~= char(text[ptr]);
|
||||
ptr += 1;
|
||||
if (char(text[ptr-1])=="\\" and ptr<text_size) {
|
||||
s ~= char(text[ptr]);
|
||||
ptr += 1;
|
||||
}
|
||||
}
|
||||
token.content=s;
|
||||
token.type=j_str;
|
||||
} elsif(isnum(c)) {
|
||||
var s=c;
|
||||
ptr+=1;
|
||||
token.type=_j_str;
|
||||
} elsif (isnum(c)) {
|
||||
var s = c;
|
||||
ptr += 1;
|
||||
while(ptr<text_size and ((isnum(char(text[ptr])) or char(text[ptr])=='.'))) {
|
||||
s~=char(text[ptr]);
|
||||
ptr+=1;
|
||||
s ~= char(text[ptr]);
|
||||
ptr += 1;
|
||||
}
|
||||
ptr-=1;
|
||||
token.content=num(s);
|
||||
token.type=j_num;
|
||||
} elsif(isid(c)) {
|
||||
var s=c;
|
||||
ptr+=1;
|
||||
ptr -= 1;
|
||||
token.content = num(s);
|
||||
token.type = _j_num;
|
||||
} elsif (isid(c)) {
|
||||
var s = c;
|
||||
ptr += 1;
|
||||
while(ptr<text_size and (isid(char(text[ptr])) or isnum(char(text[ptr])))) {
|
||||
s~=char(text[ptr]);
|
||||
ptr+=1;
|
||||
s ~= char(text[ptr]);
|
||||
ptr += 1;
|
||||
}
|
||||
ptr-=1;
|
||||
token.content=s;
|
||||
token.type=j_id;
|
||||
ptr -= 1;
|
||||
token.content = s;
|
||||
token.type = _j_id;
|
||||
}
|
||||
ptr+=1;
|
||||
ptr += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
var match=func(type) {
|
||||
var match = func(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();
|
||||
return;
|
||||
}
|
||||
|
||||
var member=func(hash) {
|
||||
var name=token.content;
|
||||
if(token.type==j_rbrace) {
|
||||
var member = func(hash) {
|
||||
var name = token.content;
|
||||
if(token.type==_j_rbrace) {
|
||||
return;
|
||||
}
|
||||
if(token.type==j_str) {
|
||||
match(j_str);
|
||||
if(token.type==_j_str) {
|
||||
match(_j_str);
|
||||
} else {
|
||||
match(j_id);
|
||||
match(_j_id);
|
||||
}
|
||||
match(j_colon);
|
||||
if(token.type==j_lbrace) {
|
||||
hash[name]=hash_gen();
|
||||
} elsif(token.type==j_lbrkt) {
|
||||
hash[name]=vec_gen();
|
||||
} elsif(token.type==j_str or token.type==j_num) {
|
||||
hash[name]=token.content;
|
||||
match(_j_colon);
|
||||
if(token.type==_j_lbrace) {
|
||||
hash[name] = hash_gen();
|
||||
} elsif(token.type==_j_lbrkt) {
|
||||
hash[name] = vec_gen();
|
||||
} elsif(token.type==_j_str or token.type==_j_num) {
|
||||
hash[name] = token.content;
|
||||
next();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var hash_gen=func() {
|
||||
var hash={};
|
||||
match(j_lbrace);
|
||||
var hash_gen = func() {
|
||||
var hash = {};
|
||||
match(_j_lbrace);
|
||||
member(hash);
|
||||
while(token.type==j_comma) {
|
||||
match(j_comma);
|
||||
while(token.type==_j_comma) {
|
||||
match(_j_comma);
|
||||
member(hash);
|
||||
}
|
||||
match(j_rbrace);
|
||||
match(_j_rbrace);
|
||||
return hash;
|
||||
}
|
||||
|
||||
var vec_gen=func() {
|
||||
var vec=[];
|
||||
match(j_lbrkt);
|
||||
if(token.type==j_lbrace) {
|
||||
append(vec,hash_gen());
|
||||
} elsif(token.type==j_lbrkt) {
|
||||
append(vec,vec_gen());
|
||||
} elsif(token.type==j_str or token.type==j_num) {
|
||||
append(vec,token.content);
|
||||
var vec_gen = func() {
|
||||
var vec = [];
|
||||
match(_j_lbrkt);
|
||||
if(token.type==_j_lbrace) {
|
||||
append(vec, hash_gen());
|
||||
} elsif(token.type==_j_lbrkt) {
|
||||
append(vec, vec_gen());
|
||||
} elsif(token.type==_j_str or token.type==_j_num) {
|
||||
append(vec, token.content);
|
||||
next();
|
||||
}
|
||||
while(token.type==j_comma) {
|
||||
match(j_comma);
|
||||
if(token.type==j_lbrace) {
|
||||
append(vec,hash_gen());
|
||||
} elsif(token.type==j_lbrkt) {
|
||||
append(vec,vec_gen());
|
||||
} elsif(token.type==j_str or token.type==j_num) {
|
||||
append(vec,token.content);
|
||||
while(token.type==_j_comma) {
|
||||
match(_j_comma);
|
||||
if(token.type==_j_lbrace) {
|
||||
append(vec, hash_gen());
|
||||
} elsif(token.type==_j_lbrkt) {
|
||||
append(vec, vec_gen());
|
||||
} elsif(token.type==_j_str or token.type==_j_num) {
|
||||
append(vec, token.content);
|
||||
next();
|
||||
}
|
||||
}
|
||||
match(j_rbrkt);
|
||||
match(_j_rbrkt);
|
||||
return vec;
|
||||
}
|
||||
|
||||
@@ -220,10 +221,10 @@ var JSON=func() {
|
||||
get(str);
|
||||
next();
|
||||
|
||||
if(token.type==j_lbrkt) {
|
||||
var res=vec_gen();
|
||||
if (token.type==_j_lbrkt) {
|
||||
var res = vec_gen();
|
||||
} else {
|
||||
var res=hash_gen();
|
||||
var res = hash_gen();
|
||||
}
|
||||
|
||||
init();
|
||||
@@ -232,55 +233,55 @@ var JSON=func() {
|
||||
};
|
||||
}();
|
||||
|
||||
JSON.stringify=func(object) {
|
||||
JSON.stringify = func(object) {
|
||||
if(typeof(object)!="hash" and typeof(object)!="vec") {
|
||||
println("JSON.stringify: must use hashmap or vector");
|
||||
return "[]";
|
||||
}
|
||||
|
||||
var s="";
|
||||
var gen=func(elem) {
|
||||
var t=typeof(elem);
|
||||
var s = "";
|
||||
var gen = func(elem) {
|
||||
var t = typeof(elem);
|
||||
if(t=="num") {
|
||||
s~=str(elem);
|
||||
s ~= str(elem);
|
||||
} elsif(t=="str") {
|
||||
s~='"'~elem~'"';
|
||||
s ~= '"'~elem~'"';
|
||||
} elsif(t=="vec") {
|
||||
vgen(elem);
|
||||
} elsif(t=="hash") {
|
||||
hgen(elem);
|
||||
} else {
|
||||
s~='"undefined"';
|
||||
s ~= '"undefined"';
|
||||
}
|
||||
}
|
||||
|
||||
var vgen=func(v) {
|
||||
s~="[";
|
||||
var vsize=size(v);
|
||||
for(var i=0;i<vsize;i+=1) {
|
||||
var vgen = func(v) {
|
||||
s ~= "[";
|
||||
var vsize = size(v);
|
||||
for(var i = 0; i<vsize; i += 1) {
|
||||
gen(v[i]);
|
||||
if(i!=vsize-1) {
|
||||
if (i!=vsize-1) {
|
||||
s~=",";
|
||||
}
|
||||
}
|
||||
s~="]";
|
||||
s ~= "]";
|
||||
}
|
||||
|
||||
var hgen=func(h) {
|
||||
s~="{";
|
||||
var k=keys(h);
|
||||
var vsize=size(k);
|
||||
for(var i=0;i<vsize;i+=1) {
|
||||
s~=k[i]~":";
|
||||
var hgen = func(h) {
|
||||
s ~= "{";
|
||||
var k = keys(h);
|
||||
var vsize = size(k);
|
||||
for(var i = 0; i<vsize; i += 1) {
|
||||
s ~= k[i]~":";
|
||||
gen(h[k[i]]);
|
||||
if(i!=vsize-1) {
|
||||
s~=",";
|
||||
if (i!=vsize-1) {
|
||||
s ~= ",";
|
||||
}
|
||||
}
|
||||
s~="}";
|
||||
s ~= "}";
|
||||
}
|
||||
|
||||
if(typeof(object)=="vec") {
|
||||
if (typeof(object)=="vec") {
|
||||
vgen(object);
|
||||
} else {
|
||||
hgen(object);
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
# padding.nas
|
||||
# ValKmjolnir 2022/9/4
|
||||
|
||||
var leftpad=func(s,len,char=" "){
|
||||
if(typeof(s)=="num")
|
||||
s=str(s);
|
||||
var strlen=size(s);
|
||||
for(var i=strlen;i<len;i+=1)
|
||||
s=char~s;
|
||||
var leftpad = func(s, len, char=" ") {
|
||||
if (typeof(s)=="num") {
|
||||
s = str(s);
|
||||
}
|
||||
var strlen = size(s);
|
||||
for(var i = strlen; i<len; i += 1) {
|
||||
s = char~s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
var rightpad=func(s,len,char=" "){
|
||||
if(typeof(s)=="num")
|
||||
s=str(s);
|
||||
var strlen=size(s);
|
||||
for(var i=strlen;i<len;i+=1)
|
||||
s~=char;
|
||||
var rightpad = func(s, len, char=" ") {
|
||||
if (typeof(s)=="num") {
|
||||
s = str(s);
|
||||
}
|
||||
var strlen = size(s);
|
||||
for(var i = strlen; i<len; i += 1) {
|
||||
s ~= char;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
193
std/props.nas
193
std/props.nas
@@ -10,42 +10,152 @@
|
||||
# available in C++; just use node.getNode(path).whatever() instead.
|
||||
#
|
||||
|
||||
# unfinished
|
||||
var getprop = func {}
|
||||
var fgcommand = func {}
|
||||
var _globals = func {}
|
||||
var _createCondition = func {}
|
||||
var _new = func {}
|
||||
var string = {};
|
||||
var _getNode = func {};
|
||||
var _getParent = func {};
|
||||
var _getChildren = func {};
|
||||
var _setChildren = func {};
|
||||
var _alias = func {};
|
||||
var _equals = func {};
|
||||
var _unalias = func {};
|
||||
var _adjustValue = func {};
|
||||
var _setDoubleValue = func {};
|
||||
var _setIntValue = func {};
|
||||
var _setBoolValue = func {};
|
||||
var _toggleBoolValue = func {};
|
||||
var _setValue = func {};
|
||||
var _setValues = func {};
|
||||
var _getAttribute = func {};
|
||||
var _setAttribute = func {};
|
||||
var _getValue = func {};
|
||||
var _getType = func {};
|
||||
var _isNumeric = func {};
|
||||
var _isInt = func {};
|
||||
var _getIndex = func {};
|
||||
var _getName = func {};
|
||||
var _getAliasTarget = func {};
|
||||
var _getChild = func {};
|
||||
var _addChild = func {};
|
||||
var _addChildren = func {};
|
||||
var _removeChild = func {};
|
||||
var _removeChildren = func {};
|
||||
var _removeAllChildren = func {};
|
||||
var _new = func {
|
||||
return {
|
||||
type: "",
|
||||
name: "",
|
||||
value: nil,
|
||||
parent_list:[],
|
||||
child_list:[]
|
||||
};
|
||||
}
|
||||
|
||||
var _fg_props_globals = _new();
|
||||
var _globals = func {
|
||||
return _fg_props_globals;
|
||||
}
|
||||
|
||||
var getprop = func(arg...) {
|
||||
die("unimplemented");
|
||||
}
|
||||
|
||||
var addcommand = func(name, code) {
|
||||
die("unimplemented");
|
||||
}
|
||||
|
||||
var fgcommand = func(name, args = nil) {
|
||||
die("unimplemented");
|
||||
}
|
||||
|
||||
var _createCondition = func {
|
||||
die("unimplemented");
|
||||
}
|
||||
|
||||
var _getNode = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _getParent = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _getChildren = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _setChildren = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _alias = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _equals = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _unalias = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _adjustValue = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _setDoubleValue = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _setIntValue = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _setBoolValue = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _toggleBoolValue = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _setValue = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _setValues = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _getAttribute = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _setAttribute = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _getValue = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _getType = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _isNumeric = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _isInt = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _getIndex = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _getName = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _getAliasTarget = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _getChild = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _addChild = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _addChildren = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _removeChild = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _removeChildren = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
var _removeAllChildren = func {
|
||||
die("unimplemented");
|
||||
};
|
||||
|
||||
|
||||
var Node = {
|
||||
@@ -190,8 +300,9 @@ var Node = {
|
||||
#
|
||||
Node.new = func(values = nil) {
|
||||
var result = wrapNode(_new());
|
||||
if(ishash(values))
|
||||
if(ishash(values)) {
|
||||
result.setValues(values);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -331,14 +442,18 @@ var wrap = func(node) {
|
||||
# Node object and its _g (ghost) field set to the specified object.
|
||||
# Nasal's literal syntax can be pleasingly terse. I like that. :)
|
||||
#
|
||||
var wrapNode = func(node) { { parents : [Node], _g : node } }
|
||||
var wrapNode = func(node) {
|
||||
return { parents : [Node], _g : node };
|
||||
}
|
||||
|
||||
##
|
||||
# Global property tree. Set once at initialization. Is that OK?
|
||||
# Does anything ever call globals.set_props() from C++? May need to
|
||||
# turn this into a function if so.
|
||||
#
|
||||
var globals = wrapNode(_globals());
|
||||
var globals = func {
|
||||
return wrapNode(_globals());
|
||||
}();
|
||||
|
||||
##
|
||||
# Shortcut for props.globals.getNode().
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
# string.nas
|
||||
# ValKmjolnir 2022/10/5
|
||||
|
||||
var join=func(sep, vec){
|
||||
var join = func(sep, vec){
|
||||
var len = size(vec);
|
||||
var res = "";
|
||||
for(var i = 0; i<len; i+=1) {
|
||||
for(var i = 0; i<len; i += 1) {
|
||||
res ~= vec[i];
|
||||
res ~= (i==len-1? "":sep);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ var count=func(s,c){
|
||||
}
|
||||
|
||||
var column=func(number){
|
||||
number=number>=1000?substr(str(number/1000),0,3)~'k':str(number);
|
||||
number=number>=1000?substr(str(number/1000),0,4)~'k':str(number);
|
||||
return padding.leftpad(number,6);
|
||||
}
|
||||
|
||||
|
||||
@@ -159,4 +159,46 @@ for(var i=10;i<1e6;i*=10) {
|
||||
println("cartesian");
|
||||
for(var i=100;i<600;i+=100) {
|
||||
cartesian(i);
|
||||
}
|
||||
|
||||
var person_data = [];
|
||||
var person = func(name, age) {
|
||||
append(person_data, [name, age]);
|
||||
return person_data;
|
||||
}
|
||||
|
||||
var know_data = [];
|
||||
var know = func(name_a, name_b) {
|
||||
append(know_data, [name_a, name_b]);
|
||||
}
|
||||
|
||||
person("a", 1);
|
||||
person("b", 2);
|
||||
person("c", 3);
|
||||
person("d", 4);
|
||||
|
||||
know("a", "b");
|
||||
know("b", "c");
|
||||
know("c", "d");
|
||||
know("d", "a");
|
||||
|
||||
# maybe_know(a, b) :- know(a, tmp), know(tmp, b).
|
||||
var maybe_know_data = [];
|
||||
|
||||
var temp = [];
|
||||
|
||||
foreach(var i; know_data) {
|
||||
foreach(var j; know_data) {
|
||||
append(temp, [i[0], i[1], j[0], j[1]]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach(var i; temp) {
|
||||
if (!cmp(i[1], i[2])) {
|
||||
append(maybe_know_data, [i, i[0], i[3]]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach(var res; maybe_know_data) {
|
||||
println(res[0], " -> ", res[1], " ", res[2]);
|
||||
}
|
||||
@@ -48,28 +48,31 @@ http.establish("127.0.0.1",8080);
|
||||
|
||||
var highlight_style="
|
||||
<style>
|
||||
body{
|
||||
body {
|
||||
background: #303030;
|
||||
}
|
||||
pre{
|
||||
pre {
|
||||
background: #303030;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: small;
|
||||
color: #d4d4d4;
|
||||
}
|
||||
code{
|
||||
code {
|
||||
color: white;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: small;
|
||||
text-align: left;
|
||||
}
|
||||
code.key{color: #f895e7;}
|
||||
code.id{color: #8abef0;}
|
||||
code.opr{color: #f895e7;}
|
||||
code.brace{color: #eafd70;}
|
||||
code.str{color: #a5ffd0;}
|
||||
code.num{color: #ff9a41;}
|
||||
code.note{color: #808080;}
|
||||
code.key {
|
||||
color: #f895e7;
|
||||
font-weight: bold;
|
||||
}
|
||||
code.id {color: #8abef0;}
|
||||
code.opr {color: #f895e7;}
|
||||
code.brace {color: #eafd70;}
|
||||
code.str {color: #a5ffd0;}
|
||||
code.num {color: #ff9a41;}
|
||||
code.note {color: #808080;}
|
||||
</style>";
|
||||
|
||||
var html_read_file=func(filename){
|
||||
|
||||
76
tools/repl.nas
Normal file
76
tools/repl.nas
Normal file
@@ -0,0 +1,76 @@
|
||||
# experimental repl
|
||||
# 2023/8/19 by ValKmjolnir
|
||||
|
||||
var help = func() {
|
||||
println(" .help | show help");
|
||||
println(" .exit | quit the REPL");
|
||||
println(" .quit | quit the REPL");
|
||||
println(" .clear | clear the screen");
|
||||
println();
|
||||
}
|
||||
|
||||
var content = [];
|
||||
var log_cache = "";
|
||||
|
||||
println("Nasal: This is experimental REPL");
|
||||
println("Tips : \";\" is automatically added at the end of the input line.");
|
||||
help();
|
||||
|
||||
var count_bracket = func(line) {
|
||||
var len = size(line);
|
||||
var count = 0;
|
||||
for(var i = 0; i < len; i += 1) {
|
||||
if (line[i] == "{"[0]) {
|
||||
count += 1;
|
||||
} elsif (line[i] == "}"[0]) {
|
||||
count -= 1;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
while(1) {
|
||||
var line = readline(">>> ");
|
||||
if (!size(line)) {
|
||||
continue;
|
||||
}
|
||||
if (line == ".exit" or line == ".quit") {
|
||||
break;
|
||||
} elsif (line == ".help") {
|
||||
println();
|
||||
help();
|
||||
continue;
|
||||
} elsif (line == ".clear") {
|
||||
print("\ec");
|
||||
continue;
|
||||
} elsif (line[0] == "."[0]) {
|
||||
println("no such command \"", line, "\", input \".help\" for help\n");
|
||||
continue;
|
||||
}
|
||||
var in_bracket_level = count_bracket(line);
|
||||
while(in_bracket_level > 0) {
|
||||
var temp_line = readline("... ");
|
||||
in_bracket_level += count_bracket(temp_line);
|
||||
line ~= temp_line ~ "\n";
|
||||
}
|
||||
|
||||
append(content, line);
|
||||
|
||||
var source = "";
|
||||
foreach(var i; content) {
|
||||
source ~= i ~ ";\n";
|
||||
}
|
||||
|
||||
io.fout(".temp.nas", source);
|
||||
var result = system("nasal .temp.nas > .temp.log");
|
||||
if (result != 0) {
|
||||
pop(content);
|
||||
continue;
|
||||
}
|
||||
|
||||
var log = io.readfile(".temp.log");
|
||||
if (size(log) and size(log) != size(log_cache)) {
|
||||
println(substr(log, size(log_cache), size(log)), "\n");
|
||||
log_cache = log;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user