Merge pull request #44 from ValKmjolnir/develop
✨ support `??` and `?.` operators & fix utf8 output on MSVC
This commit is contained in:
commit
5aa99f396b
|
@ -10,6 +10,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED True)
|
|||
set(CMAKE_CXX_FLAGS_RELEASE_INIT "-Wshadow -Wall")
|
||||
|
||||
add_compile_options(-fPIC)
|
||||
# MSVC needs this command option to really enable utf-8 output
|
||||
if(MSVC)
|
||||
add_compile_options(/utf-8)
|
||||
endif()
|
||||
|
||||
# generate release executables
|
||||
set(CMAKE_BUILD_TYPE "Release")
|
||||
|
|
7
makefile
7
makefile
|
@ -112,7 +112,12 @@ build/nasal_type.o:\
|
|||
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
|
||||
build/nasal_gc.o:\
|
||||
src/nasal.h\
|
||||
src/util/util.h\
|
||||
src/nasal_type.h\
|
||||
src/nasal_gc.h\
|
||||
src/nasal_gc.cpp | build
|
||||
$(CXX) $(CXXFLAGS) src/nasal_gc.cpp -o build/nasal_gc.o
|
||||
|
||||
build/nasal_import.o: \
|
||||
|
|
|
@ -185,6 +185,7 @@ bool ast_dumper::visit_binary_operator(binary_operator* node) {
|
|||
case binary_operator::binary_type::leq: std::cout << "<="; break;
|
||||
case binary_operator::binary_type::condition_and: std::cout << "and"; break;
|
||||
case binary_operator::binary_type::condition_or: std::cout << "or"; break;
|
||||
case binary_operator::binary_type::null_chain: std::cout << "??"; break;
|
||||
}
|
||||
std::cout << "\"" << format_location(node);
|
||||
push_indent();
|
||||
|
@ -241,6 +242,13 @@ bool ast_dumper::visit_call_hash(call_hash* node) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_null_access(null_access* node) {
|
||||
dump_indent();
|
||||
std::cout << "null_access " << node->get_field();
|
||||
std::cout << format_location(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_dumper::visit_call_vector(call_vector* node) {
|
||||
dump_indent();
|
||||
std::cout << "call_vector";
|
||||
|
|
|
@ -45,7 +45,7 @@ private:
|
|||
|
||||
std::string format_location(expr* node) {
|
||||
std::stringstream ss;
|
||||
ss << " → [";
|
||||
ss << " ⇒ [";
|
||||
node->get_location().dump_begin(ss);
|
||||
ss << "]\n";
|
||||
return ss.str();
|
||||
|
@ -70,6 +70,7 @@ public:
|
|||
bool visit_unary_operator(unary_operator*) override;
|
||||
bool visit_call_expr(call_expr*) override;
|
||||
bool visit_call_hash(call_hash*) override;
|
||||
bool visit_null_access(null_access*) override;
|
||||
bool visit_call_vector(call_vector*) override;
|
||||
bool visit_call_function(call_function*) override;
|
||||
bool visit_slice_vector(slice_vector*) override;
|
||||
|
@ -90,10 +91,10 @@ public:
|
|||
|
||||
public:
|
||||
void dump(code_block* root) {
|
||||
util::windows_code_page_manager wcpm;
|
||||
wcpm.set_utf8_output();
|
||||
util::windows_code_page_manager wm;
|
||||
wm.set_utf8_output();
|
||||
root->accept(this);
|
||||
wcpm.restore_code_page();
|
||||
wm.restore_code_page();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -116,6 +116,10 @@ bool ast_visitor::visit_call_hash(call_hash* node) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_null_access(null_access* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ast_visitor::visit_call_vector(call_vector* node) {
|
||||
for(auto i : node->get_slices()) {
|
||||
i->accept(this);
|
||||
|
|
|
@ -26,6 +26,7 @@ public:
|
|||
virtual bool visit_unary_operator(unary_operator*);
|
||||
virtual bool visit_call_expr(call_expr*);
|
||||
virtual bool visit_call_hash(call_hash*);
|
||||
virtual bool visit_null_access(null_access*);
|
||||
virtual bool visit_call_vector(call_vector*);
|
||||
virtual bool visit_call_function(call_function*);
|
||||
virtual bool visit_slice_vector(slice_vector*);
|
||||
|
|
|
@ -23,4 +23,40 @@ cli_config parse(const std::vector<std::string>& args) {
|
|||
return result;
|
||||
}
|
||||
|
||||
std::ostream& help(std::ostream& out) {
|
||||
out
|
||||
<< "\n"
|
||||
<< " ,--#-,\n"
|
||||
<< "<3 / \\____\\ <3\n"
|
||||
<< " |_|__A_|\n"
|
||||
<< "\nnasal <option>\n"
|
||||
<< "option:\n"
|
||||
<< " -h, --help | get help.\n"
|
||||
<< " -v, --version | get version.\n"
|
||||
<< " -r, --repl | use repl interpreter.\n"
|
||||
<< "\nnasal [option] <file> [argv]\n"
|
||||
<< "option:\n"
|
||||
<< " -a, --ast | view ast after link/optimize process.\n"
|
||||
<< " --raw-ast | view ast without after-processing.\n"
|
||||
<< " -c, --code | view generated bytecode.\n"
|
||||
<< " -s, --symbol | show analysed symbol info.\n"
|
||||
<< " -e, --exec | execute directly.\n"
|
||||
<< " -t, --time | show execute time.\n"
|
||||
<< " -d, --detail | get detail info.\n"
|
||||
<< " -f, --ref-file | get referenced files.\n"
|
||||
<< " -dbg, --debug | debug mode.\n"
|
||||
<< " --prof | show profiling result, "
|
||||
<< "available under debug mode.\n"
|
||||
<< " --prof-all | show profiling result of all files, "
|
||||
<< "available under debug mode.\n"
|
||||
<< " --limit | use limited execution mode "
|
||||
<< "(readonly api enabled).\n"
|
||||
<< "file:\n"
|
||||
<< " <filename> | execute file.\n"
|
||||
<< "argv:\n"
|
||||
<< " <args> | cmd arguments used in program.\n"
|
||||
<< "\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@
|
|||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
namespace nasal::cli {
|
||||
|
||||
|
@ -67,4 +68,6 @@ const std::unordered_map<std::string, option> cli_options = {
|
|||
|
||||
cli_config parse(const std::vector<std::string>&);
|
||||
|
||||
std::ostream& help(std::ostream&);
|
||||
|
||||
}
|
50
src/main.cpp
50
src/main.cpp
|
@ -18,45 +18,9 @@
|
|||
#include "repl/repl.h"
|
||||
#include "cli/cli.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <thread>
|
||||
#include <cstdlib>
|
||||
|
||||
std::ostream& help(std::ostream& out) {
|
||||
out
|
||||
<< "\n"
|
||||
<< " ,--#-,\n"
|
||||
<< "<3 / \\____\\ <3\n"
|
||||
<< " |_|__A_|\n"
|
||||
<< "\nnasal <option>\n"
|
||||
<< "option:\n"
|
||||
<< " -h, --help | get help.\n"
|
||||
<< " -v, --version | get version.\n"
|
||||
<< " -r, --repl | use repl interpreter.\n"
|
||||
<< "\nnasal [option] <file> [argv]\n"
|
||||
<< "option:\n"
|
||||
<< " -a, --ast | view ast after link/optimize process.\n"
|
||||
<< " --raw-ast | view ast without after-processing.\n"
|
||||
<< " -c, --code | view generated bytecode.\n"
|
||||
<< " -s, --symbol | show analysed symbol info.\n"
|
||||
<< " -e, --exec | execute directly.\n"
|
||||
<< " -t, --time | show execute time.\n"
|
||||
<< " -d, --detail | get detail info.\n"
|
||||
<< " -f, --ref-file | get referenced files.\n"
|
||||
<< " -dbg, --debug | debug mode.\n"
|
||||
<< " --prof | show profiling result, available in debug mode.\n"
|
||||
<< " --prof-all | show profiling result of all files, "
|
||||
<< "available under debug mode.\n"
|
||||
<< " --limit | use limited execution mode "
|
||||
<< "(readonly api enabled).\n"
|
||||
<< "file:\n"
|
||||
<< " <filename> | execute file.\n"
|
||||
<< "argv:\n"
|
||||
<< " <args> | cmd arguments used in program.\n"
|
||||
<< "\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& logo(std::ostream& out) {
|
||||
out
|
||||
<< "\n"
|
||||
|
@ -67,7 +31,8 @@ std::ostream& logo(std::ostream& out) {
|
|||
<< " \\_\\ \\/ \\__,_|___/\\__,_|_|\n"
|
||||
<< "\n"
|
||||
<< "ver : " << __nasver__
|
||||
<< " " << nasal::util::get_platform() << " " << nasal::util::get_arch()
|
||||
<< " " << nasal::util::get_platform()
|
||||
<< " " << nasal::util::get_arch()
|
||||
<< " (" << __DATE__ << " " << __TIME__ << ")\n"
|
||||
<< "std : c++ " << __cplusplus << "\n"
|
||||
<< "core : " << std::thread::hardware_concurrency() << " core(s)\n"
|
||||
|
@ -75,9 +40,9 @@ std::ostream& logo(std::ostream& out) {
|
|||
<< "repo : https://gitee.com/valkmjolnir/Nasal-Interpreter\n"
|
||||
<< "wiki : https://wiki.flightgear.org/Nasal_scripting_language\n"
|
||||
<< "\n"
|
||||
<< "presented by fgprc members\n"
|
||||
<< " - http://fgprc.org\n"
|
||||
<< " - http://fgprc.org.cn\n"
|
||||
<< "presented by fgprc members:\n"
|
||||
<< " - http://fgprc.org\n"
|
||||
<< " - http://fgprc.org.cn\n"
|
||||
<< "\n"
|
||||
<< "input <nasal -h> to get help .\n\n";
|
||||
return out;
|
||||
|
@ -96,7 +61,8 @@ std::ostream& version(std::ostream& out) {
|
|||
}
|
||||
|
||||
out << "nasal version " << __nasver__;
|
||||
out << " " << nasal::util::get_platform() << " " << nasal::util::get_arch();
|
||||
out << " " << nasal::util::get_platform();
|
||||
out << " " << nasal::util::get_arch();
|
||||
out << " (" << __DATE__ << " " << __TIME__ << ")\n";
|
||||
return out;
|
||||
}
|
||||
|
@ -195,7 +161,7 @@ i32 main(i32 argc, const char* argv[]) {
|
|||
// run directly or show help
|
||||
if (argc==2) {
|
||||
if (config.has(nasal::cli::option::cli_help)) {
|
||||
std::clog << help;
|
||||
std::clog << nasal::cli::help;
|
||||
} else if (config.has(nasal::cli::option::cli_version)) {
|
||||
std::clog << version;
|
||||
} else if (config.has(nasal::cli::option::cli_repl_mode)) {
|
||||
|
|
|
@ -173,6 +173,10 @@ void call_hash::accept(ast_visitor* visitor) {
|
|||
visitor->visit_call_hash(this);
|
||||
}
|
||||
|
||||
void null_access::accept(ast_visitor* visitor) {
|
||||
visitor->visit_null_access(this);
|
||||
}
|
||||
|
||||
call_vector::~call_vector() {
|
||||
for(auto i : calls) {
|
||||
delete i;
|
||||
|
|
|
@ -23,6 +23,7 @@ enum class expr_type {
|
|||
ast_pair, // pair of key and value in hashmap
|
||||
ast_call, // mark a sub-tree of calling an identifier
|
||||
ast_callh, // id.name
|
||||
ast_null_access, // id?.name
|
||||
ast_callv, // id[index]
|
||||
ast_callf, // id()
|
||||
ast_subvec, // id[index:index]
|
||||
|
@ -302,7 +303,8 @@ public:
|
|||
bitwise_xor,
|
||||
bitwise_and,
|
||||
condition_and,
|
||||
condition_or
|
||||
condition_or,
|
||||
null_chain
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -389,6 +391,19 @@ public:
|
|||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class null_access: public call {
|
||||
private:
|
||||
std::string field;
|
||||
|
||||
public:
|
||||
null_access(const span& location, const std::string& name):
|
||||
call(location, expr_type::ast_null_access),
|
||||
field(name) {}
|
||||
~null_access() override = default;
|
||||
const std::string& get_field() const {return field;}
|
||||
void accept(ast_visitor*) override;
|
||||
};
|
||||
|
||||
class call_vector: public call {
|
||||
private:
|
||||
std::vector<slice_vector*> calls;
|
||||
|
|
|
@ -339,6 +339,8 @@ void codegen::call_gen(call_expr* node) {
|
|||
call_vector_gen(reinterpret_cast<call_vector*>(i)); break;
|
||||
case expr_type::ast_callf:
|
||||
call_func_gen(reinterpret_cast<call_function*>(i)); break;
|
||||
case expr_type::ast_null_access:
|
||||
null_access_gen(reinterpret_cast<null_access*>(i)); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
@ -382,6 +384,24 @@ void codegen::call_hash_gen(call_hash* node) {
|
|||
emit(op_callh, const_string_map.at(node->get_field()), node->get_location());
|
||||
}
|
||||
|
||||
void codegen::null_access_gen(null_access* node) {
|
||||
regist_string(node->get_field());
|
||||
|
||||
emit(op_dup, 0, node->get_location());
|
||||
emit(op_pnil, 0, node->get_location());
|
||||
emit(op_eq, 0, node->get_location());
|
||||
|
||||
const auto jmp_false_point = code.size();
|
||||
emit(op_jf, 0, node->get_location());
|
||||
|
||||
const auto jmp_direct_point = code.size();
|
||||
emit(op_jmp, 0, node->get_location());
|
||||
|
||||
code[jmp_false_point].num = code.size();
|
||||
emit(op_callh, const_string_map.at(node->get_field()), node->get_location());
|
||||
code[jmp_direct_point].num = code.size();
|
||||
}
|
||||
|
||||
void codegen::call_vector_gen(call_vector* node) {
|
||||
// maybe this place can use callv-const if ast's first child is ast_num
|
||||
if (node->get_slices().size()==1 &&
|
||||
|
@ -455,6 +475,8 @@ void codegen::mcall(expr* node) {
|
|||
call_vector_gen(reinterpret_cast<call_vector*>(tmp)); break;
|
||||
case expr_type::ast_callf:
|
||||
call_func_gen(reinterpret_cast<call_function*>(tmp)); break;
|
||||
case expr_type::ast_null_access:
|
||||
null_access_gen(reinterpret_cast<null_access*>(tmp)); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
@ -467,6 +489,8 @@ void codegen::mcall(expr* node) {
|
|||
mcall_vec(reinterpret_cast<call_vector*>(tmp)); break;
|
||||
case expr_type::ast_callf:
|
||||
die("bad left-value: function call", tmp->get_location()); break;
|
||||
case expr_type::ast_null_access:
|
||||
die("bad left-value: null access test", tmp->get_location()); break;
|
||||
default:
|
||||
die("bad left-value: unknown call", tmp->get_location()); break;
|
||||
}
|
||||
|
@ -1059,6 +1083,9 @@ void codegen::binary_gen(binary_operator* node) {
|
|||
calc_gen(node->get_right());
|
||||
emit(op_btand, 0, node->get_location());
|
||||
return;
|
||||
case binary_operator::binary_type::null_chain:
|
||||
null_chain_gen(node);
|
||||
return;
|
||||
default: break;
|
||||
}
|
||||
switch(node->get_operator_type()) {
|
||||
|
@ -1174,6 +1201,23 @@ void codegen::binary_gen(binary_operator* node) {
|
|||
}
|
||||
}
|
||||
|
||||
void codegen::null_chain_gen(binary_operator* node) {
|
||||
calc_gen(node->get_left());
|
||||
emit(op_pnil, 0, node->get_location());
|
||||
emit(op_eq, 0, node->get_location());
|
||||
|
||||
const auto jmp_false_point = code.size();
|
||||
emit(op_jf, 0, node->get_location());
|
||||
|
||||
calc_gen(node->get_right());
|
||||
const auto jmp_direct_point = code.size();
|
||||
emit(op_jmp, 0, node->get_location());
|
||||
|
||||
code[jmp_false_point].num = code.size();
|
||||
emit(op_pop, 0, node->get_location());
|
||||
code[jmp_direct_point].num = code.size();
|
||||
}
|
||||
|
||||
void codegen::trino_gen(ternary_operator* node) {
|
||||
calc_gen(node->get_condition());
|
||||
usize label_jump_false = code.size();
|
||||
|
|
|
@ -123,6 +123,7 @@ private:
|
|||
void call_gen(call_expr*);
|
||||
void call_identifier(identifier*);
|
||||
void call_hash_gen(call_hash*);
|
||||
void null_access_gen(null_access*);
|
||||
void call_vector_gen(call_vector*);
|
||||
void call_func_gen(call_function*);
|
||||
void mcall(expr*);
|
||||
|
@ -148,6 +149,7 @@ private:
|
|||
void and_gen(binary_operator*);
|
||||
void unary_gen(unary_operator*);
|
||||
void binary_gen(binary_operator*);
|
||||
void null_chain_gen(binary_operator*);
|
||||
void trino_gen(ternary_operator*);
|
||||
void calc_gen(expr*);
|
||||
void repl_mode_info_output_gen(expr*);
|
||||
|
|
|
@ -46,8 +46,9 @@ void operand_line_counter::dump_operand_count() const {
|
|||
if (!rate) {
|
||||
break;
|
||||
}
|
||||
std::clog << " " << oprand_name_table[i.first] << " : ";
|
||||
std::clog << i.second << " (" << rate << "%)\n";
|
||||
std::clog << " ";
|
||||
std::clog << operand_name_table.at(static_cast<op_code_type>(i.first));
|
||||
std::clog << " : " << i.second << " (" << rate << "%)\n";
|
||||
}
|
||||
std::clog << " total : " << total << '\n';
|
||||
}
|
||||
|
|
|
@ -40,55 +40,6 @@ public:
|
|||
};
|
||||
|
||||
class dbg: public vm {
|
||||
private:
|
||||
typedef void (dbg::*nasal_vm_func)();
|
||||
const nasal_vm_func operand_function[op_ret + 1] = {
|
||||
nullptr, &dbg::o_repl,
|
||||
&dbg::o_intl, &dbg::o_loadg,
|
||||
&dbg::o_loadl, &dbg::o_loadu,
|
||||
&dbg::o_pnum, &dbg::o_pnil,
|
||||
&dbg::o_pstr, &dbg::o_newv,
|
||||
&dbg::o_newh, &dbg::o_newf,
|
||||
&dbg::o_happ, &dbg::o_para,
|
||||
&dbg::o_deft, &dbg::o_dyn,
|
||||
&dbg::o_lnot, &dbg::o_usub,
|
||||
&dbg::o_bnot, &dbg::o_btor,
|
||||
&dbg::o_btxor, &dbg::o_btand,
|
||||
&dbg::o_add, &dbg::o_sub,
|
||||
&dbg::o_mul, &dbg::o_div,
|
||||
&dbg::o_lnk, &dbg::o_addc,
|
||||
&dbg::o_subc, &dbg::o_mulc,
|
||||
&dbg::o_divc, &dbg::o_lnkc,
|
||||
&dbg::o_addeq, &dbg::o_subeq,
|
||||
&dbg::o_muleq, &dbg::o_diveq,
|
||||
&dbg::o_lnkeq, &dbg::o_bandeq,
|
||||
&dbg::o_boreq, &dbg::o_bxoreq,
|
||||
&dbg::o_addeqc, &dbg::o_subeqc,
|
||||
&dbg::o_muleqc, &dbg::o_diveqc,
|
||||
&dbg::o_lnkeqc, &dbg::o_addecp,
|
||||
&dbg::o_subecp, &dbg::o_mulecp,
|
||||
&dbg::o_divecp, &dbg::o_lnkecp,
|
||||
&dbg::o_meq, &dbg::o_eq,
|
||||
&dbg::o_neq, &dbg::o_less,
|
||||
&dbg::o_leq, &dbg::o_grt,
|
||||
&dbg::o_geq, &dbg::o_lessc,
|
||||
&dbg::o_leqc, &dbg::o_grtc,
|
||||
&dbg::o_geqc, &dbg::o_pop,
|
||||
&dbg::o_jmp, &dbg::o_jt,
|
||||
&dbg::o_jf, &dbg::o_cnt,
|
||||
&dbg::o_findex, &dbg::o_feach,
|
||||
&dbg::o_callg, &dbg::o_calll,
|
||||
&dbg::o_upval, &dbg::o_callv,
|
||||
&dbg::o_callvi, &dbg::o_callh,
|
||||
&dbg::o_callfv, &dbg::o_callfh,
|
||||
&dbg::o_callb, &dbg::o_slcbeg,
|
||||
&dbg::o_slcend, &dbg::o_slc,
|
||||
&dbg::o_slc2, &dbg::o_mcallg,
|
||||
&dbg::o_mcalll, &dbg::o_mupval,
|
||||
&dbg::o_mcallv, &dbg::o_mcallh,
|
||||
&dbg::o_ret
|
||||
};
|
||||
|
||||
private:
|
||||
enum class cmd_kind {
|
||||
cmd_error,
|
||||
|
|
125
src/nasal_gc.cpp
125
src/nasal_gc.cpp
|
@ -1,4 +1,5 @@
|
|||
#include "nasal_gc.h"
|
||||
#include "nasal_gc.h"
|
||||
#include "util/util.h"
|
||||
|
||||
namespace nasal {
|
||||
|
||||
|
@ -258,15 +259,27 @@ void gc::clear() {
|
|||
}
|
||||
|
||||
void gc::info() const {
|
||||
util::windows_code_page_manager wm;
|
||||
wm.set_utf8_output();
|
||||
|
||||
using std::left;
|
||||
using std::setw;
|
||||
using std::setfill;
|
||||
using std::setprecision;
|
||||
|
||||
const char* used_table_name[] = {
|
||||
"object type", "gc count", "alloc count", "memory size",
|
||||
"detail", "time spend", "gc time", "avg time", "max gc",
|
||||
"max mark", "max sweep", nullptr
|
||||
"object type",
|
||||
"gc count",
|
||||
"alloc count",
|
||||
"memory size",
|
||||
"detail",
|
||||
"time spend",
|
||||
"gc time",
|
||||
"avg time",
|
||||
"max gc",
|
||||
"max mark",
|
||||
"max sweep",
|
||||
nullptr
|
||||
};
|
||||
const char* name[] = {
|
||||
"string",
|
||||
|
@ -297,21 +310,33 @@ void gc::info() const {
|
|||
len = std::to_string(size[i]).length();
|
||||
indent = indent<len? len:indent;
|
||||
}
|
||||
auto indent_string = std::string("--");
|
||||
auto indent_string = std::string("──");
|
||||
for(usize i = 0; i<indent; ++i) {
|
||||
indent_string += "-";
|
||||
indent_string += "─";
|
||||
}
|
||||
const auto last_line = "+" + indent_string + "+" +
|
||||
indent_string + "-" + indent_string + "-" + indent_string + "+";
|
||||
indent_string = indent_string + "+" +
|
||||
indent_string + "+" + indent_string + "+" + indent_string;
|
||||
const auto first_line = "╭" + indent_string + "┬" +
|
||||
indent_string + "─" +
|
||||
indent_string + "─" +
|
||||
indent_string + "╮";
|
||||
const auto mid_line = "├" + indent_string + "┼" +
|
||||
indent_string + "┼" +
|
||||
indent_string + "┼" +
|
||||
indent_string + "┤";
|
||||
const auto another_mid_line = "├" + indent_string + "┼" +
|
||||
indent_string + "┴" +
|
||||
indent_string + "┴" +
|
||||
indent_string + "┤";
|
||||
const auto last_line = "╰" + indent_string + "┴" +
|
||||
indent_string + "─" +
|
||||
indent_string + "─" +
|
||||
indent_string + "╯";
|
||||
|
||||
std::clog << "\n+" << indent_string << "+\n";
|
||||
std::clog << "| " << left << setw(indent) << setfill(' ') << "object type";
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << "gc count";
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << "alloc count";
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << "memory size";
|
||||
std::clog << " |\n+" << indent_string << "+\n";
|
||||
std::clog << "\n" << first_line << "\n";
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "object type";
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << "gc count";
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << "alloc count";
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << "memory size";
|
||||
std::clog << " │\n" << mid_line << "\n";
|
||||
|
||||
double total = 0;
|
||||
for(u8 i = 0; i<gc_type_size; ++i) {
|
||||
|
@ -319,46 +344,54 @@ void gc::info() const {
|
|||
continue;
|
||||
}
|
||||
total += static_cast<f64>(gcnt[i]);
|
||||
std::clog << "| " << left << setw(indent) << setfill(' ') << name[i];
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << gcnt[i];
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << acnt[i];
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << size[i];
|
||||
std::clog << " |\n";
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << name[i];
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << gcnt[i];
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << acnt[i];
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << size[i];
|
||||
std::clog << " │\n";
|
||||
}
|
||||
std::clog << "+" << indent_string << "+\n";
|
||||
std::clog << mid_line << "\n";
|
||||
|
||||
auto den = std::chrono::high_resolution_clock::duration::period::den;
|
||||
std::clog << "| " << left << setw(indent) << setfill(' ') << "detail";
|
||||
std::clog << " | " << left << setw(indent) << setfill(' ') << "time spend";
|
||||
std::clog << " | " << left << setw(indent) << setfill('x') << "x";
|
||||
std::clog << " | " << left << setw(indent) << setfill('x') << "x";
|
||||
std::clog << " |\n+" << indent_string << "+\n";
|
||||
const auto den = std::chrono::high_resolution_clock::duration::period::den;
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "detail";
|
||||
std::clog << " │ " << left << setw(indent) << setfill(' ') << "time spend";
|
||||
std::clog << " │ " << left << setw(indent) << setfill('x') << "x";
|
||||
std::clog << " │ " << left << setw(indent) << setfill('x') << "x";
|
||||
std::clog << " │\n" << another_mid_line << "\n";
|
||||
|
||||
std::clog << "| " << left << setw(indent) << setfill(' ') << "gc time";
|
||||
std::clog << " | " << setw(indent-3) << std::setprecision(4) << worktime*1.0/den*1000 << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "|\n";
|
||||
const auto gc_time = worktime*1.0/den*1000;
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "gc time";
|
||||
std::clog << " │ " << setw(indent-3) << setprecision(4) << gc_time << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "│\n";
|
||||
|
||||
std::clog << "| " << left << setw(indent) << setfill(' ') << "avg time";
|
||||
std::clog << " | " << setw(indent-3) << std::setprecision(4) << worktime*1.0/den*1000/total << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "|\n";
|
||||
const auto avg_time = worktime*1.0/den*1000/total;
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "avg time";
|
||||
std::clog << " │ " << setw(indent-3) << setprecision(4) << avg_time << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "│\n";
|
||||
|
||||
std::clog << "| " << left << setw(indent) << setfill(' ') << "max gc";
|
||||
std::clog << " | " << setw(indent-3) << std::setprecision(4) << max_time*1.0/den*1000 << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "|\n";
|
||||
const auto max_gc = max_time*1.0/den*1000;
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "max gc";
|
||||
std::clog << " │ " << setw(indent-3) << setprecision(4) << max_gc << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "│\n";
|
||||
|
||||
std::clog << "| " << left << setw(indent) << setfill(' ') << "max mark";
|
||||
std::clog << " | " << setw(indent-3) << std::setprecision(4) << max_mark_time*1.0/den*1000 << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "|\n";
|
||||
const auto max_mark = max_mark_time*1.0/den*1000;
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "max mark";
|
||||
std::clog << " │ " << setw(indent-3) << setprecision(4) << max_mark << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "│\n";
|
||||
|
||||
std::clog << "| " << left << setw(indent) << setfill(' ') << "max sweep";
|
||||
std::clog << " | " << setw(indent-3) << std::setprecision(4) << max_sweep_time*1.0/den*1000 << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "|\n";
|
||||
const auto max_sweep = max_sweep_time*1.0/den*1000;
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "max sweep";
|
||||
std::clog << " │ " << setw(indent-3) << setprecision(4) << max_sweep << " ms";
|
||||
std::clog << setw(indent*2+7) << " " << "│\n";
|
||||
|
||||
std::clog << "| " << left << setw(indent) << setfill(' ') << "concurrent";
|
||||
std::clog << " | " << setw(indent) << (flag_concurrent_mark_triggered? "true":"false");
|
||||
std::clog << setw(indent*2+7) << " " << "|\n";
|
||||
std::clog << "│ " << left << setw(indent) << setfill(' ') << "concurrent";
|
||||
std::clog << " │ " << setw(indent)
|
||||
<< (flag_concurrent_mark_triggered? "true":"false");
|
||||
std::clog << setw(indent*2+7) << " " << "│\n";
|
||||
|
||||
std::clog << last_line << "\n";
|
||||
|
||||
wm.restore_code_page();
|
||||
}
|
||||
|
||||
var gc::alloc(const vm_type type) {
|
||||
|
|
|
@ -35,12 +35,16 @@ bool lexer::is_str(char c) {
|
|||
return c=='\'' || c=='\"' || c=='`';
|
||||
}
|
||||
|
||||
bool lexer::is_quesmark(char c) {
|
||||
return c=='?';
|
||||
}
|
||||
|
||||
bool lexer::is_single_opr(char c) {
|
||||
return (
|
||||
c=='(' || c==')' || c=='[' || c==']' ||
|
||||
c=='{' || c=='}' || c==',' || c==';' ||
|
||||
c==':' || c=='?' || c=='`' || c=='@' ||
|
||||
c=='%' || c=='$' || c=='\\'
|
||||
c==':' || c=='`' || c=='@' || c=='%' ||
|
||||
c=='$' || c=='\\'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -103,6 +107,8 @@ void lexer::open(const std::string& file) {
|
|||
}
|
||||
|
||||
tok lexer::get_type(const std::string& str) {
|
||||
// search token type from mapper
|
||||
// if cannot find, just return null
|
||||
return token_mapper.count(str)? token_mapper.at(str):tok::tk_null;
|
||||
}
|
||||
|
||||
|
@ -333,6 +339,24 @@ token lexer::str_gen() {
|
|||
};
|
||||
}
|
||||
|
||||
token lexer::quesmark_gen() {
|
||||
u64 begin_line = line;
|
||||
u64 begin_column = column;
|
||||
std::string str(1, res[ptr]);
|
||||
++column;
|
||||
++ptr;
|
||||
if (ptr < res.size() && (res[ptr]=='?' || res[ptr]=='.')) {
|
||||
str += res[ptr];
|
||||
++column;
|
||||
++ptr;
|
||||
}
|
||||
return {
|
||||
{begin_line, begin_column, line, column, filename},
|
||||
get_type(str),
|
||||
str
|
||||
};
|
||||
}
|
||||
|
||||
token lexer::single_opr() {
|
||||
u64 begin_line = line;
|
||||
u64 begin_column = column;
|
||||
|
@ -398,6 +422,8 @@ const error& lexer::scan(const std::string& file) {
|
|||
toks.push_back(num_gen());
|
||||
} else if (is_str(res[ptr])) {
|
||||
toks.push_back(str_gen());
|
||||
} else if (is_quesmark(res[ptr])) {
|
||||
toks.push_back(quesmark_gen());
|
||||
} else if (is_single_opr(res[ptr])) {
|
||||
toks.push_back(single_opr());
|
||||
} else if (res[ptr]=='.') {
|
||||
|
|
|
@ -50,6 +50,8 @@ enum class tok {
|
|||
tk_dot, // .
|
||||
tk_ellipsis, // ...
|
||||
tk_quesmark, // ?
|
||||
tk_quesques, // ??
|
||||
tk_quesdot, // ?.
|
||||
tk_colon, // :
|
||||
tk_add, // operator +
|
||||
tk_sub, // operator -
|
||||
|
@ -79,9 +81,10 @@ enum class tok {
|
|||
};
|
||||
|
||||
struct token {
|
||||
span loc; // location
|
||||
tok type; // token type
|
||||
span loc; // location
|
||||
tok type; // token type
|
||||
std::string str; // content
|
||||
|
||||
token() = default;
|
||||
token(const token&) = default;
|
||||
};
|
||||
|
@ -94,10 +97,12 @@ private:
|
|||
std::string filename;
|
||||
std::string res;
|
||||
|
||||
private:
|
||||
error err;
|
||||
u64 invalid_char;
|
||||
std::vector<token> toks;
|
||||
|
||||
private:
|
||||
const std::unordered_map<std::string, tok> token_mapper = {
|
||||
{"use" , tok::tk_use },
|
||||
{"true" , tok::tk_true },
|
||||
|
@ -128,6 +133,8 @@ private:
|
|||
{"." , tok::tk_dot },
|
||||
{"..." , tok::tk_ellipsis},
|
||||
{"?" , tok::tk_quesmark},
|
||||
{"??" , tok::tk_quesques},
|
||||
{"?." , tok::tk_quesdot },
|
||||
{":" , tok::tk_colon },
|
||||
{"+" , tok::tk_add },
|
||||
{"-" , tok::tk_sub },
|
||||
|
@ -155,6 +162,7 @@ private:
|
|||
{">=" , tok::tk_geq }
|
||||
};
|
||||
|
||||
private:
|
||||
tok get_type(const std::string&);
|
||||
bool skip(char);
|
||||
bool is_id(char);
|
||||
|
@ -162,6 +170,7 @@ private:
|
|||
bool is_oct(char);
|
||||
bool is_dec(char);
|
||||
bool is_str(char);
|
||||
bool is_quesmark(char);
|
||||
bool is_single_opr(char);
|
||||
bool is_calc_opr(char);
|
||||
|
||||
|
@ -173,14 +182,17 @@ private:
|
|||
token id_gen();
|
||||
token num_gen();
|
||||
token str_gen();
|
||||
token quesmark_gen();
|
||||
token single_opr();
|
||||
token dots();
|
||||
token calc_opr();
|
||||
|
||||
public:
|
||||
lexer(): line(1), column(0), ptr(0),
|
||||
filename(""), res(""), invalid_char(0) {}
|
||||
filename(""), res(""),
|
||||
invalid_char(0) {}
|
||||
const error& scan(const std::string&);
|
||||
const std::vector<token>& result() const {return toks;}
|
||||
const auto& result() const {return toks;}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -3,31 +3,6 @@
|
|||
|
||||
namespace nasal {
|
||||
|
||||
const char* oprand_name_table[] = {
|
||||
"exit ", "repl ", "intl ", "loadg ",
|
||||
"loadl ", "loadu ", "pnum ", "pnil ",
|
||||
"pstr ", "newv ", "newh ", "newf ",
|
||||
"happ ", "para ", "def ", "dyn ",
|
||||
"lnot ", "usub ", "bitnot", "bitor ",
|
||||
"bitxor", "bitand", "add ", "sub ",
|
||||
"mult ", "div ", "lnk ", "addc ",
|
||||
"subc ", "multc ", "divc ", "lnkc ",
|
||||
"addeq ", "subeq ", "muleq ", "diveq ",
|
||||
"lnkeq ", "bandeq", "boreq ", "bxoreq",
|
||||
"addeqc", "subeqc", "muleqc", "diveqc",
|
||||
"lnkeqc", "addecp", "subecp", "mulecp",
|
||||
"divecp", "lnkecp", "meq ", "eq ",
|
||||
"neq ", "less ", "leq ", "grt ",
|
||||
"geq ", "lessc ", "leqc ", "grtc ",
|
||||
"geqc ", "pop ", "jmp ", "jt ",
|
||||
"jf ", "cnt ", "findx ", "feach ",
|
||||
"callg ", "calll ", "upval ", "callv ",
|
||||
"callvi", "callh ", "callfv", "callfh",
|
||||
"callb ", "slcbeg", "slcend", "slice ",
|
||||
"slice2", "mcallg", "mcalll", "mupval",
|
||||
"mcallv", "mcallh", "ret "
|
||||
};
|
||||
|
||||
void codestream::set(const f64* number_list,
|
||||
const std::string* string_list,
|
||||
const nasal_builtin_table* native_table,
|
||||
|
@ -58,7 +33,7 @@ void codestream::dump(std::ostream& out) const {
|
|||
}
|
||||
|
||||
// dump operand name
|
||||
out << " " << oprand_name_table[op] << " ";
|
||||
out << " " << operand_name_table.at(static_cast<op_code_type>(op)) << " ";
|
||||
|
||||
switch(op) {
|
||||
case op_addeq:
|
||||
|
|
|
@ -14,6 +14,7 @@ enum op_code_type: u8 {
|
|||
op_loadg, // load global value
|
||||
op_loadl, // load local value
|
||||
op_loadu, // load upvalue
|
||||
op_dup, // copy value on stack top
|
||||
op_pnum, // push constant number to the stack
|
||||
op_pnil, // push constant nil to the stack
|
||||
op_pstr, // push constant std::string to the stack
|
||||
|
@ -97,6 +98,97 @@ enum op_code_type: u8 {
|
|||
op_ret // return
|
||||
};
|
||||
|
||||
static std::unordered_map<op_code_type, std::string> operand_name_table = {
|
||||
{op_code_type::op_exit, "exit "},
|
||||
{op_code_type::op_repl, "repl "},
|
||||
{op_code_type::op_intl, "intl "},
|
||||
{op_code_type::op_loadg, "loadg "},
|
||||
{op_code_type::op_loadl, "loadl "},
|
||||
{op_code_type::op_loadu, "loadu "},
|
||||
{op_code_type::op_dup, "dup "},
|
||||
{op_code_type::op_pnum, "pnum "},
|
||||
{op_code_type::op_pnil, "pnil "},
|
||||
{op_code_type::op_pstr, "pstr "},
|
||||
{op_code_type::op_newv, "newv "},
|
||||
{op_code_type::op_newh, "newh "},
|
||||
{op_code_type::op_newf, "newf "},
|
||||
{op_code_type::op_happ, "happ "},
|
||||
{op_code_type::op_para, "para "},
|
||||
{op_code_type::op_deft, "def "},
|
||||
{op_code_type::op_dyn, "dyn "},
|
||||
{op_code_type::op_lnot, "lnot "},
|
||||
{op_code_type::op_usub, "usub "},
|
||||
{op_code_type::op_bnot, "bitnot"},
|
||||
{op_code_type::op_btor, "bitor "},
|
||||
{op_code_type::op_btxor, "bitxor"},
|
||||
{op_code_type::op_btand, "bitand"},
|
||||
{op_code_type::op_add, "add "},
|
||||
{op_code_type::op_sub, "sub "},
|
||||
{op_code_type::op_mul, "mult "},
|
||||
{op_code_type::op_div, "div "},
|
||||
{op_code_type::op_lnk, "lnk "},
|
||||
{op_code_type::op_addc, "addc "},
|
||||
{op_code_type::op_subc, "subc "},
|
||||
{op_code_type::op_mulc, "multc "},
|
||||
{op_code_type::op_divc, "divc "},
|
||||
{op_code_type::op_lnkc, "lnkc "},
|
||||
{op_code_type::op_addeq, "addeq "},
|
||||
{op_code_type::op_subeq, "subeq "},
|
||||
{op_code_type::op_muleq, "muleq "},
|
||||
{op_code_type::op_diveq, "diveq "},
|
||||
{op_code_type::op_lnkeq, "lnkeq "},
|
||||
{op_code_type::op_btandeq, "bandeq"},
|
||||
{op_code_type::op_btoreq, "boreq "},
|
||||
{op_code_type::op_btxoreq, "bxoreq"},
|
||||
{op_code_type::op_addeqc, "addeqc"},
|
||||
{op_code_type::op_subeqc, "subeqc"},
|
||||
{op_code_type::op_muleqc, "muleqc"},
|
||||
{op_code_type::op_diveqc, "diveqc"},
|
||||
{op_code_type::op_lnkeqc, "lnkeqc"},
|
||||
{op_code_type::op_addecp, "addecp"},
|
||||
{op_code_type::op_subecp, "subecp"},
|
||||
{op_code_type::op_mulecp, "mulecp"},
|
||||
{op_code_type::op_divecp, "divecp"},
|
||||
{op_code_type::op_lnkecp, "lnkecp"},
|
||||
{op_code_type::op_meq, "meq "},
|
||||
{op_code_type::op_eq, "eq "},
|
||||
{op_code_type::op_neq, "neq "},
|
||||
{op_code_type::op_less, "less "},
|
||||
{op_code_type::op_leq, "leq "},
|
||||
{op_code_type::op_grt, "grt "},
|
||||
{op_code_type::op_geq, "geq "},
|
||||
{op_code_type::op_lessc, "lessc "},
|
||||
{op_code_type::op_leqc, "leqc "},
|
||||
{op_code_type::op_grtc, "grtc "},
|
||||
{op_code_type::op_geqc, "geqc "},
|
||||
{op_code_type::op_pop, "pop "},
|
||||
{op_code_type::op_jmp, "jmp "},
|
||||
{op_code_type::op_jt, "jt "},
|
||||
{op_code_type::op_jf, "jf "},
|
||||
{op_code_type::op_cnt, "cnt "},
|
||||
{op_code_type::op_findex, "findx "},
|
||||
{op_code_type::op_feach, "feach "},
|
||||
{op_code_type::op_callg, "callg "},
|
||||
{op_code_type::op_calll, "calll "},
|
||||
{op_code_type::op_upval, "upval "},
|
||||
{op_code_type::op_callv, "callv "},
|
||||
{op_code_type::op_callvi, "callvi"},
|
||||
{op_code_type::op_callh, "callh "},
|
||||
{op_code_type::op_callfv, "callfv"},
|
||||
{op_code_type::op_callfh, "callfh"},
|
||||
{op_code_type::op_callb, "callb "},
|
||||
{op_code_type::op_slcbeg, "slcbeg"},
|
||||
{op_code_type::op_slcend, "slcend"},
|
||||
{op_code_type::op_slc, "slice "},
|
||||
{op_code_type::op_slc2, "slice2"},
|
||||
{op_code_type::op_mcallg, "mcallg"},
|
||||
{op_code_type::op_mcalll, "mcalll"},
|
||||
{op_code_type::op_mupval, "mupval"},
|
||||
{op_code_type::op_mcallv, "mcallv"},
|
||||
{op_code_type::op_mcallh, "mcallh"},
|
||||
{op_code_type::op_ret, "ret "}
|
||||
};
|
||||
|
||||
struct opcode {
|
||||
u8 op; // opcode
|
||||
u16 fidx; // source code file index
|
||||
|
@ -127,6 +219,4 @@ public:
|
|||
|
||||
std::ostream& operator<<(std::ostream&, const codestream&);
|
||||
|
||||
extern const char* oprand_name_table[];
|
||||
|
||||
}
|
|
@ -82,7 +82,11 @@ void parse::match(tok type, const char* info) {
|
|||
case tok::tk_num: die(thisspan, "expected number"); break;
|
||||
case tok::tk_str: die(thisspan, "expected string"); break;
|
||||
case tok::tk_id: die(thisspan, "expected identifier"); break;
|
||||
default: die(thisspan, "expected \""+tokname.at(type)+"\""); break;
|
||||
default:
|
||||
die(thisspan,
|
||||
"expected \"" + token_name_mapper.at(type)+"\""
|
||||
);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -94,7 +98,8 @@ bool parse::lookahead(tok type) {
|
|||
}
|
||||
|
||||
bool parse::is_call(tok type) {
|
||||
return type==tok::tk_lcurve || type==tok::tk_lbracket || type==tok::tk_dot;
|
||||
return type==tok::tk_lcurve || type==tok::tk_lbracket ||
|
||||
type==tok::tk_dot || type==tok::tk_quesdot;
|
||||
}
|
||||
|
||||
bool parse::check_comma(const tok* panic_set) {
|
||||
|
@ -564,7 +569,7 @@ expr* parse::and_expr() {
|
|||
}
|
||||
|
||||
expr* parse::cmp_expr() {
|
||||
auto node = additive_expr();
|
||||
auto node = null_chain_expr();
|
||||
while(tok::tk_cmpeq<=toks[ptr].type && toks[ptr].type<=tok::tk_geq) {
|
||||
auto tmp = new binary_operator(toks[ptr].loc);
|
||||
switch(toks[ptr].type) {
|
||||
|
@ -578,6 +583,21 @@ expr* parse::cmp_expr() {
|
|||
}
|
||||
tmp->set_left(node);
|
||||
match(toks[ptr].type);
|
||||
tmp->set_right(null_chain_expr());
|
||||
update_location(tmp);
|
||||
node = tmp;
|
||||
}
|
||||
update_location(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
expr* parse::null_chain_expr() {
|
||||
auto node = additive_expr();
|
||||
while(lookahead(tok::tk_quesques)) {
|
||||
auto tmp = new binary_operator(toks[ptr].loc);
|
||||
tmp->set_operator_type(binary_operator::binary_type::null_chain);
|
||||
tmp->set_left(node);
|
||||
match(tok::tk_quesques);
|
||||
tmp->set_right(additive_expr());
|
||||
update_location(tmp);
|
||||
node = tmp;
|
||||
|
@ -718,6 +738,7 @@ call* parse::call_scalar() {
|
|||
case tok::tk_lcurve: return callf(); break;
|
||||
case tok::tk_lbracket: return callv(); break;
|
||||
case tok::tk_dot: return callh(); break;
|
||||
case tok::tk_quesdot: return null_access_call(); break;
|
||||
default: break;
|
||||
}
|
||||
// unreachable
|
||||
|
@ -733,6 +754,15 @@ call_hash* parse::callh() {
|
|||
return node;
|
||||
}
|
||||
|
||||
null_access* parse::null_access_call() {
|
||||
const auto& begin_loc = toks[ptr].loc;
|
||||
match(tok::tk_quesdot);
|
||||
auto node = new null_access(begin_loc, toks[ptr].str);
|
||||
update_location(node);
|
||||
match(tok::tk_id, "expected hashmap key"); // get key
|
||||
return node;
|
||||
}
|
||||
|
||||
call_vector* parse::callv() {
|
||||
// panic set for this token is not ','
|
||||
// this is the FIRST set of subvec
|
||||
|
|
|
@ -23,7 +23,7 @@ private:
|
|||
error err;
|
||||
|
||||
private:
|
||||
const std::unordered_map<tok, std::string> tokname = {
|
||||
const std::unordered_map<tok, std::string> token_name_mapper = {
|
||||
{tok::tk_true , "true" },
|
||||
{tok::tk_false , "false" },
|
||||
{tok::tk_use , "use" },
|
||||
|
@ -53,6 +53,8 @@ private:
|
|||
{tok::tk_dot , "." },
|
||||
{tok::tk_ellipsis, "..." },
|
||||
{tok::tk_quesmark, "?" },
|
||||
{tok::tk_quesques, "??" },
|
||||
{tok::tk_quesdot , "?." },
|
||||
{tok::tk_colon , ":" },
|
||||
{tok::tk_add , "+" },
|
||||
{tok::tk_sub , "-" },
|
||||
|
@ -117,12 +119,14 @@ private:
|
|||
expr* or_expr();
|
||||
expr* and_expr();
|
||||
expr* cmp_expr();
|
||||
expr* null_chain_expr();
|
||||
expr* additive_expr();
|
||||
expr* multive_expr();
|
||||
unary_operator* unary();
|
||||
expr* scalar();
|
||||
call* call_scalar();
|
||||
call_hash* callh();
|
||||
null_access* null_access_call();
|
||||
call_vector* callv();
|
||||
call_function* callf();
|
||||
slice_vector* subvec();
|
||||
|
@ -153,8 +157,9 @@ public:
|
|||
}
|
||||
|
||||
public:
|
||||
parse(): ptr(0), in_func_depth(0), in_loop_depth(0),
|
||||
toks(nullptr), root(nullptr) {}
|
||||
parse(): ptr(0), in_func_depth(0),
|
||||
in_loop_depth(0), toks(nullptr),
|
||||
root(nullptr) {}
|
||||
~parse() {delete root;}
|
||||
const error& compile(const lexer&);
|
||||
static void easter_egg();
|
||||
|
|
164
src/nasal_vm.cpp
164
src/nasal_vm.cpp
|
@ -429,28 +429,94 @@ void vm::run(const codegen& gen,
|
|||
#ifndef _MSC_VER
|
||||
// using labels as values/computed goto
|
||||
const void* oprs[] = {
|
||||
&&vmexit, &&repl, &&intl, &&loadg,
|
||||
&&loadl, &&loadu, &&pnum, &&pnil,
|
||||
&&pstr, &&newv, &&newh, &&newf,
|
||||
&&happ, &¶, &&deft, &&dyn,
|
||||
&&lnot, &&usub, &&bnot, &&btor,
|
||||
&&btxor, &&btand, &&add, &&sub,
|
||||
&&mul, &&div, &&lnk, &&addc,
|
||||
&&subc, &&mulc, &&divc, &&lnkc,
|
||||
&&addeq, &&subeq, &&muleq, &&diveq,
|
||||
&&lnkeq, &&bandeq, &&boreq, &&bxoreq,
|
||||
&&addeqc, &&subeqc, &&muleqc, &&diveqc,
|
||||
&&lnkeqc, &&addecp, &&subecp, &&mulecp,
|
||||
&&divecp, &&lnkecp, &&meq, &&eq,
|
||||
&&neq, &&less, &&leq, &&grt,
|
||||
&&geq, &&lessc, &&leqc, &&grtc,
|
||||
&&geqc, &&pop, &&jmp, &&jt,
|
||||
&&jf, &&cnt, &&findex, &&feach,
|
||||
&&callg, &&calll, &&upval, &&callv,
|
||||
&&callvi, &&callh, &&callfv, &&callfh,
|
||||
&&callb, &&slcbeg, &&slcend, &&slc,
|
||||
&&slc2, &&mcallg, &&mcalll, &&mupval,
|
||||
&&mcallv, &&mcallh, &&ret
|
||||
&&vmexit,
|
||||
&&repl,
|
||||
&&intl,
|
||||
&&loadg,
|
||||
&&loadl,
|
||||
&&loadu,
|
||||
&&dup,
|
||||
&&pnum,
|
||||
&&pnil,
|
||||
&&pstr,
|
||||
&&newv,
|
||||
&&newh,
|
||||
&&newf,
|
||||
&&happ,
|
||||
&¶,
|
||||
&&deft,
|
||||
&&dyn,
|
||||
&&lnot,
|
||||
&&usub,
|
||||
&&bnot,
|
||||
&&btor,
|
||||
&&btxor,
|
||||
&&btand,
|
||||
&&add,
|
||||
&&sub,
|
||||
&&mul,
|
||||
&&div,
|
||||
&&lnk,
|
||||
&&addc,
|
||||
&&subc,
|
||||
&&mulc,
|
||||
&&divc,
|
||||
&&lnkc,
|
||||
&&addeq,
|
||||
&&subeq,
|
||||
&&muleq,
|
||||
&&diveq,
|
||||
&&lnkeq,
|
||||
&&bandeq,
|
||||
&&boreq,
|
||||
&&bxoreq,
|
||||
&&addeqc,
|
||||
&&subeqc,
|
||||
&&muleqc,
|
||||
&&diveqc,
|
||||
&&lnkeqc,
|
||||
&&addecp,
|
||||
&&subecp,
|
||||
&&mulecp,
|
||||
&&divecp,
|
||||
&&lnkecp,
|
||||
&&meq,
|
||||
&&eq,
|
||||
&&neq,
|
||||
&&less,
|
||||
&&leq,
|
||||
&&grt,
|
||||
&&geq,
|
||||
&&lessc,
|
||||
&&leqc,
|
||||
&&grtc,
|
||||
&&geqc,
|
||||
&&pop,
|
||||
&&jmp,
|
||||
&&jt,
|
||||
&&jf,
|
||||
&&cnt,
|
||||
&&findex,
|
||||
&&feach,
|
||||
&&callg,
|
||||
&&calll,
|
||||
&&upval,
|
||||
&&callv,
|
||||
&&callvi,
|
||||
&&callh,
|
||||
&&callfv,
|
||||
&&callfh,
|
||||
&&callb,
|
||||
&&slcbeg,
|
||||
&&slcend,
|
||||
&&slc,
|
||||
&&slc2,
|
||||
&&mcallg,
|
||||
&&mcalll,
|
||||
&&mupval,
|
||||
&&mcallv,
|
||||
&&mcallh,
|
||||
&&ret
|
||||
};
|
||||
std::vector<const void*> code;
|
||||
for(const auto& i : gen.codes()) {
|
||||
|
@ -460,56 +526,9 @@ void vm::run(const codegen& gen,
|
|||
// goto the first operand
|
||||
goto *code[ctx.pc];
|
||||
#else
|
||||
typedef void (vm::*nafunc)();
|
||||
const nafunc oprs[] = {
|
||||
nullptr, &vm::o_repl,
|
||||
&vm::o_intl, &vm::o_loadg,
|
||||
&vm::o_loadl, &vm::o_loadu,
|
||||
&vm::o_pnum, &vm::o_pnil,
|
||||
&vm::o_pstr, &vm::o_newv,
|
||||
&vm::o_newh, &vm::o_newf,
|
||||
&vm::o_happ, &vm::o_para,
|
||||
&vm::o_deft, &vm::o_dyn,
|
||||
&vm::o_lnot, &vm::o_usub,
|
||||
&vm::o_bnot, &vm::o_btor,
|
||||
&vm::o_btxor, &vm::o_btand,
|
||||
&vm::o_add, &vm::o_sub,
|
||||
&vm::o_mul, &vm::o_div,
|
||||
&vm::o_lnk, &vm::o_addc,
|
||||
&vm::o_subc, &vm::o_mulc,
|
||||
&vm::o_divc, &vm::o_lnkc,
|
||||
&vm::o_addeq, &vm::o_subeq,
|
||||
&vm::o_muleq, &vm::o_diveq,
|
||||
&vm::o_lnkeq, &vm::o_bandeq,
|
||||
&vm::o_boreq, &vm::o_bxoreq,
|
||||
&vm::o_addeqc, &vm::o_subeqc,
|
||||
&vm::o_muleqc, &vm::o_diveqc,
|
||||
&vm::o_lnkeqc, &vm::o_addecp,
|
||||
&vm::o_subecp, &vm::o_mulecp,
|
||||
&vm::o_divecp, &vm::o_lnkecp,
|
||||
&vm::o_meq, &vm::o_eq,
|
||||
&vm::o_neq, &vm::o_less,
|
||||
&vm::o_leq, &vm::o_grt,
|
||||
&vm::o_geq, &vm::o_lessc,
|
||||
&vm::o_leqc, &vm::o_grtc,
|
||||
&vm::o_geqc, &vm::o_pop,
|
||||
&vm::o_jmp, &vm::o_jt,
|
||||
&vm::o_jf, &vm::o_cnt,
|
||||
&vm::o_findex, &vm::o_feach,
|
||||
&vm::o_callg, &vm::o_calll,
|
||||
&vm::o_upval, &vm::o_callv,
|
||||
&vm::o_callvi, &vm::o_callh,
|
||||
&vm::o_callfv, &vm::o_callfh,
|
||||
&vm::o_callb, &vm::o_slcbeg,
|
||||
&vm::o_slcend, &vm::o_slc,
|
||||
&vm::o_slc2, &vm::o_mcallg,
|
||||
&vm::o_mcalll, &vm::o_mupval,
|
||||
&vm::o_mcallv, &vm::o_mcallh,
|
||||
&vm::o_ret
|
||||
};
|
||||
std::vector<nafunc> code;
|
||||
std::vector<nasal_vm_func> code;
|
||||
for(const auto& i : gen.codes()) {
|
||||
code.push_back(oprs[i.op]);
|
||||
code.push_back(operand_function[i.op]);
|
||||
imm.push_back(i.num);
|
||||
}
|
||||
while(code[ctx.pc]) {
|
||||
|
@ -520,7 +539,7 @@ void vm::run(const codegen& gen,
|
|||
++ctx.pc;
|
||||
}
|
||||
#endif
|
||||
|
||||
// all nasal programs should end here
|
||||
vmexit:
|
||||
if (verbose) {
|
||||
ngc.info();
|
||||
|
@ -551,6 +570,7 @@ intl: exec_nodie(o_intl ); // -0
|
|||
loadg: exec_nodie(o_loadg ); // -1
|
||||
loadl: exec_nodie(o_loadl ); // -1
|
||||
loadu: exec_nodie(o_loadu ); // -1
|
||||
dup: exec_check(o_dup ); // +1
|
||||
pnum: exec_check(o_pnum ); // +1
|
||||
pnil: exec_check(o_pnil ); // +1
|
||||
pstr: exec_check(o_pstr ); // +1
|
||||
|
|
100
src/nasal_vm.h
100
src/nasal_vm.h
|
@ -93,6 +93,7 @@ protected:
|
|||
inline void o_loadg();
|
||||
inline void o_loadl();
|
||||
inline void o_loadu();
|
||||
inline void o_dup();
|
||||
inline void o_pnum();
|
||||
inline void o_pnil();
|
||||
inline void o_pstr();
|
||||
|
@ -175,6 +176,100 @@ protected:
|
|||
inline void o_mcallh();
|
||||
inline void o_ret();
|
||||
|
||||
protected:
|
||||
// for debugger and MSVC(does not support labels as values)
|
||||
typedef void (vm::*nasal_vm_func)();
|
||||
const nasal_vm_func operand_function[op_ret + 1] = {
|
||||
nullptr,
|
||||
&vm::o_repl,
|
||||
&vm::o_intl,
|
||||
&vm::o_loadg,
|
||||
&vm::o_loadl,
|
||||
&vm::o_loadu,
|
||||
&vm::o_dup,
|
||||
&vm::o_pnum,
|
||||
&vm::o_pnil,
|
||||
&vm::o_pstr,
|
||||
&vm::o_newv,
|
||||
&vm::o_newh,
|
||||
&vm::o_newf,
|
||||
&vm::o_happ,
|
||||
&vm::o_para,
|
||||
&vm::o_deft,
|
||||
&vm::o_dyn,
|
||||
&vm::o_lnot,
|
||||
&vm::o_usub,
|
||||
&vm::o_bnot,
|
||||
&vm::o_btor,
|
||||
&vm::o_btxor,
|
||||
&vm::o_btand,
|
||||
&vm::o_add,
|
||||
&vm::o_sub,
|
||||
&vm::o_mul,
|
||||
&vm::o_div,
|
||||
&vm::o_lnk,
|
||||
&vm::o_addc,
|
||||
&vm::o_subc,
|
||||
&vm::o_mulc,
|
||||
&vm::o_divc,
|
||||
&vm::o_lnkc,
|
||||
&vm::o_addeq,
|
||||
&vm::o_subeq,
|
||||
&vm::o_muleq,
|
||||
&vm::o_diveq,
|
||||
&vm::o_lnkeq,
|
||||
&vm::o_bandeq,
|
||||
&vm::o_boreq,
|
||||
&vm::o_bxoreq,
|
||||
&vm::o_addeqc,
|
||||
&vm::o_subeqc,
|
||||
&vm::o_muleqc,
|
||||
&vm::o_diveqc,
|
||||
&vm::o_lnkeqc,
|
||||
&vm::o_addecp,
|
||||
&vm::o_subecp,
|
||||
&vm::o_mulecp,
|
||||
&vm::o_divecp,
|
||||
&vm::o_lnkecp,
|
||||
&vm::o_meq,
|
||||
&vm::o_eq,
|
||||
&vm::o_neq,
|
||||
&vm::o_less,
|
||||
&vm::o_leq,
|
||||
&vm::o_grt,
|
||||
&vm::o_geq,
|
||||
&vm::o_lessc,
|
||||
&vm::o_leqc,
|
||||
&vm::o_grtc,
|
||||
&vm::o_geqc,
|
||||
&vm::o_pop,
|
||||
&vm::o_jmp,
|
||||
&vm::o_jt,
|
||||
&vm::o_jf,
|
||||
&vm::o_cnt,
|
||||
&vm::o_findex,
|
||||
&vm::o_feach,
|
||||
&vm::o_callg,
|
||||
&vm::o_calll,
|
||||
&vm::o_upval,
|
||||
&vm::o_callv,
|
||||
&vm::o_callvi,
|
||||
&vm::o_callh,
|
||||
&vm::o_callfv,
|
||||
&vm::o_callfh,
|
||||
&vm::o_callb,
|
||||
&vm::o_slcbeg,
|
||||
&vm::o_slcend,
|
||||
&vm::o_slc,
|
||||
&vm::o_slc2,
|
||||
&vm::o_mcallg,
|
||||
&vm::o_mcalll,
|
||||
&vm::o_mupval,
|
||||
&vm::o_mcallv,
|
||||
&vm::o_mcallh,
|
||||
&vm::o_ret
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
/* constructor of vm instance */
|
||||
|
@ -248,6 +343,11 @@ inline void vm::o_loadu() {
|
|||
.upval()[imm[ctx.pc]&0xffff] = (ctx.top--)[0];
|
||||
}
|
||||
|
||||
inline void vm::o_dup() {
|
||||
ctx.top[1] = ctx.top[0];
|
||||
++ctx.top;
|
||||
}
|
||||
|
||||
inline void vm::o_pnum() {
|
||||
(++ctx.top)[0] = var::num(const_number[imm[ctx.pc]]);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ var table_character_set = [
|
|||
"┷", "┳", "⊥", "﹃", "﹄", "╮", "╭", "╯", "╰", "╳"
|
||||
];
|
||||
|
||||
var selection_box = ["○", "◉", "☐", "▣"];
|
||||
var selection_box = ["○", "◉", "☐", "▣", "☑", "☒"];
|
||||
|
||||
var char_ttf=[
|
||||
[" "," "," "," "," "," "],
|
||||
|
|
|
@ -7,8 +7,8 @@ use std.io;
|
|||
use std.unix;
|
||||
use std.math;
|
||||
|
||||
var is_windows_platform=os.platform()=="windows";
|
||||
var is_macos_platform=os.platform()=="macOS";
|
||||
var is_windows_platform = os.platform()=="windows";
|
||||
var is_macos_platform = os.platform()=="macOS";
|
||||
|
||||
if (is_windows_platform) {
|
||||
runtime.windows.set_utf8_output();
|
||||
|
@ -17,72 +17,72 @@ if (is_windows_platform) {
|
|||
var cpu_stat = func() {
|
||||
if (is_windows_platform or is_macos_platform)
|
||||
return nil;
|
||||
var cpu=split("\n",io.readfile("/proc/stat"))[0];
|
||||
cpu=split(" ",cpu);
|
||||
cpu={
|
||||
name:cpu[0],
|
||||
user:cpu[1],
|
||||
nice:cpu[2],
|
||||
system:cpu[3],
|
||||
idle:cpu[4],
|
||||
iowait:cpu[5],
|
||||
irq:cpu[6],
|
||||
softirq:cpu[7],
|
||||
var cpu = split("\n", io.readfile("/proc/stat"))[0];
|
||||
cpu = split(" ", cpu);
|
||||
cpu = {
|
||||
name: cpu[0],
|
||||
user: cpu[1],
|
||||
nice: cpu[2],
|
||||
system: cpu[3],
|
||||
idle: cpu[4],
|
||||
iowait: cpu[5],
|
||||
irq: cpu[6],
|
||||
softirq: cpu[7],
|
||||
};
|
||||
return cpu;
|
||||
}
|
||||
|
||||
var cpu_occupation = func() {
|
||||
var first_in=1;
|
||||
var first_in = 1;
|
||||
while(1) {
|
||||
var cpu0=cpu_stat();
|
||||
var cpu0 = cpu_stat();
|
||||
if (first_in) {
|
||||
unix.sleep(0.1);
|
||||
first_in=0;
|
||||
first_in = 0;
|
||||
} else {
|
||||
for(var i=0;i<10;i+=1) {
|
||||
for(var i = 0; i < 10; i += 1) {
|
||||
unix.sleep(0.1);
|
||||
coroutine.yield(nil);
|
||||
}
|
||||
}
|
||||
var cpu1=cpu_stat();
|
||||
var cpu1 = cpu_stat();
|
||||
if (is_windows_platform or is_macos_platform) {
|
||||
coroutine.yield(0);
|
||||
continue;
|
||||
}
|
||||
var t0=cpu0.user+cpu0.nice+cpu0.system+cpu0.idle+cpu0.iowait+cpu0.irq+cpu0.softirq;
|
||||
var t1=cpu1.user+cpu1.nice+cpu1.system+cpu1.idle+cpu1.iowait+cpu1.irq+cpu1.softirq;
|
||||
var interval=cpu1.idle-cpu0.idle;
|
||||
coroutine.yield(t0==t1?0:(1-interval/(t1-t0))*100);
|
||||
var t0 = cpu0.user+cpu0.nice+cpu0.system+cpu0.idle+cpu0.iowait+cpu0.irq+cpu0.softirq;
|
||||
var t1 = cpu1.user+cpu1.nice+cpu1.system+cpu1.idle+cpu1.iowait+cpu1.irq+cpu1.softirq;
|
||||
var interval = cpu1.idle-cpu0.idle;
|
||||
coroutine.yield(t0==t1? 0:(1-interval/(t1-t0))*100);
|
||||
}
|
||||
}
|
||||
|
||||
var mem_occupation = func() {
|
||||
if (is_windows_platform or is_macos_platform)
|
||||
return {MemTotal:math.inf,MemFree:math.inf};
|
||||
var meminfo=split("\n",io.readfile("/proc/meminfo"));
|
||||
var mem_res={};
|
||||
forindex(var i;meminfo) {
|
||||
var tmp=split(" ",meminfo[i])[0:1];
|
||||
tmp[0]=substr(tmp[0],0,size(tmp[0])-1);
|
||||
mem_res[tmp[0]]=num(tmp[1]);
|
||||
var meminfo = split("\n",io.readfile("/proc/meminfo"));
|
||||
var mem_res = {};
|
||||
forindex(var i; meminfo) {
|
||||
var tmp = split(" ", meminfo[i])[0:1];
|
||||
tmp[0] = substr(tmp[0], 0, size(tmp[0])-1);
|
||||
mem_res[tmp[0]] = num(tmp[1]);
|
||||
}
|
||||
return mem_res;
|
||||
}
|
||||
|
||||
var random_generator = func() {
|
||||
var rise=[" ","▁","▂","▃","▄","▅","▆","▇","█"];
|
||||
var total=0;
|
||||
var statistics=[];
|
||||
setsize(statistics,70);
|
||||
var rise = [" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"];
|
||||
var total = 0;
|
||||
var statistics = [];
|
||||
setsize(statistics, 70);
|
||||
while(1) {
|
||||
for(var i=0;i<10;i+=1) {
|
||||
total+=1;
|
||||
var u=rand()*rand()*(rand()>0.5?-1:1);
|
||||
statistics[int(size(statistics)/2+u*size(statistics)/2)]+=1;
|
||||
}
|
||||
var s=["","",""];
|
||||
foreach(var st;statistics) {
|
||||
var s = ["", "", ""];
|
||||
foreach(var st; statistics) {
|
||||
var max_rate=100/size(statistics);
|
||||
var rate=st/total*100;
|
||||
for(var i=size(s)-1;i>=0;i-=1) {
|
||||
|
@ -97,12 +97,12 @@ var random_generator = func() {
|
|||
}
|
||||
var tmp="";
|
||||
for(var i=0;i<size(statistics);i+=1) {
|
||||
tmp~="-";
|
||||
tmp~="─";
|
||||
}
|
||||
println("\e[16;1H \e[32m|",s[0],"|\e[0m");
|
||||
println("\e[17;1H \e[32m|",s[1],"|\e[0m");
|
||||
println("\e[18;1H \e[32m|",s[2],"|\e[0m");
|
||||
println("\e[19;1H \e[32m+"~tmp~"+\e[0m");
|
||||
println("\e[16;1H \e[32m│", s[0], "│\e[0m");
|
||||
println("\e[17;1H \e[32m│", s[1], "│\e[0m");
|
||||
println("\e[18;1H \e[32m│", s[2], "│\e[0m");
|
||||
println("\e[19;1H \e[32m╰", tmp, "╯\e[0m");
|
||||
coroutine.yield();
|
||||
}
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ func() {
|
|||
|
||||
var tmp="";
|
||||
for(var i=0;i<70;i+=1) {
|
||||
tmp~="-";
|
||||
tmp~="─";
|
||||
}
|
||||
|
||||
var s=["","",""];
|
||||
|
@ -181,11 +181,11 @@ func() {
|
|||
}
|
||||
}
|
||||
}
|
||||
println("\e[7;1H \e[32m+"~tmp~"+\e[0m");
|
||||
println("\e[8;1H \e[32m|",s[0],"|\e[0m");
|
||||
println("\e[9;1H \e[32m|",s[1],"|\e[0m");
|
||||
println("\e[10;1H \e[32m|",s[2],"|\e[0m");
|
||||
println("\e[11;1H \e[32m+"~tmp~"+\e[0m");
|
||||
println("\e[7;1H \e[32m╭"~tmp~"╮\e[0m");
|
||||
println("\e[8;1H \e[32m│",s[0],"│\e[0m");
|
||||
println("\e[9;1H \e[32m│",s[1],"│\e[0m");
|
||||
println("\e[10;1H \e[32m│",s[2],"│\e[0m");
|
||||
println("\e[11;1H \e[32m├"~tmp~"┤\e[0m");
|
||||
|
||||
var s=["","",""];
|
||||
foreach(var occ;mem_occupation_log) {
|
||||
|
@ -201,10 +201,10 @@ func() {
|
|||
}
|
||||
}
|
||||
}
|
||||
println("\e[12;1H \e[32m|",s[0],"|\e[0m");
|
||||
println("\e[13;1H \e[32m|",s[1],"|\e[0m");
|
||||
println("\e[14;1H \e[32m|",s[2],"|\e[0m");
|
||||
println("\e[15;1H \e[32m+"~tmp~"+\e[0m");
|
||||
println("\e[12;1H \e[32m│",s[0],"│\e[0m");
|
||||
println("\e[13;1H \e[32m│",s[1],"│\e[0m");
|
||||
println("\e[14;1H \e[32m│",s[2],"│\e[0m");
|
||||
println("\e[15;1H \e[32m├"~tmp~"┤\e[0m");
|
||||
|
||||
println("\e[20;1H Press 'q' to quit.");
|
||||
}
|
||||
|
|
|
@ -267,3 +267,16 @@ for(var i = 1; i<=10; i += 1) {
|
|||
die("test failed: expect " ~ i ~ ", but get " ~ closure_tester[1]());
|
||||
}
|
||||
}
|
||||
|
||||
func() {
|
||||
var a = nil;
|
||||
var b = nil;
|
||||
var c = nil;
|
||||
println(a??b??c??"a??b??c?? -> should print this text");
|
||||
|
||||
var a = {b: 2};
|
||||
println(a?.b); # should be 2
|
||||
|
||||
var a = nil;
|
||||
println(a?.b); # should be nil
|
||||
}();
|
||||
|
|
Loading…
Reference in New Issue