✨ improve error info when lack arguments
in function call
This commit is contained in:
parent
80f9fc5842
commit
ecfb679218
|
@ -4,7 +4,7 @@
|
|||
|
||||

|
||||

|
||||

|
||||

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

|
||||

|
||||

|
||||

|
||||
[](../LICENSE)
|
||||
|
||||
> 这篇文档包含多语言版本: [__中文__](../doc/README_zh.md) | [__English__](../README.md)
|
||||
|
|
|
@ -28,7 +28,7 @@ var builtin_cocreate(var* local, gc& ngc) {
|
|||
|
||||
cort.ctx.top[0] = nil;
|
||||
cort.ctx.localr = cort.ctx.top+1;
|
||||
cort.ctx.top = cort.ctx.localr+func.func().lsize;
|
||||
cort.ctx.top = cort.ctx.localr+func.func().local_size;
|
||||
cort.ctx.localr[0] = func.func().local[0];
|
||||
cort.ctx.top[0] = nil; // old upvalr
|
||||
cort.ctx.top++;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#ifndef __nasver
|
||||
#define __nasver "11.0"
|
||||
#define __nasver "11.1"
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
|
|
|
@ -93,7 +93,7 @@ std::ostream& operator<<(std::ostream& out, nas_hash& hash) {
|
|||
}
|
||||
|
||||
void nas_func::clear() {
|
||||
dpara = -1;
|
||||
dynamic_parameter_index = -1;
|
||||
local.clear();
|
||||
upval.clear();
|
||||
keys.clear();
|
||||
|
|
|
@ -146,22 +146,26 @@ struct nas_hash {
|
|||
};
|
||||
|
||||
struct nas_func {
|
||||
i32 dpara; // dynamic parameter name index in hash.
|
||||
i32 dynamic_parameter_index; // dynamic parameter name index in hash.
|
||||
u32 entry; // pc will set to entry-1 to call this function
|
||||
u32 psize; // used to load default parameters to a new function
|
||||
u32 lsize; // used to expand memory space for local values on stack
|
||||
u32 parameter_size; // used to load default parameters to a new function
|
||||
u32 local_size; // used to expand memory space for local values on stack
|
||||
std::vector<var> local; // local scope with default value(var)
|
||||
std::vector<var> upval; // closure
|
||||
std::unordered_map<u32,u32> keys; // parameter table, u32 begins from 1
|
||||
|
||||
nas_func(): dpara(-1), entry(0), psize(0), lsize(0) {}
|
||||
// parameter table, u32 begins from 1
|
||||
std::unordered_map<std::string, u32> keys;
|
||||
|
||||
nas_func():
|
||||
dynamic_parameter_index(-1), entry(0),
|
||||
parameter_size(0), local_size(0) {}
|
||||
void clear();
|
||||
};
|
||||
|
||||
struct nas_upval {
|
||||
public:
|
||||
/* on stack, use these variables */
|
||||
bool onstk;
|
||||
bool on_stack;
|
||||
u32 size;
|
||||
var* stk;
|
||||
|
||||
|
@ -169,14 +173,14 @@ public:
|
|||
std::vector<var> elems;
|
||||
|
||||
public:
|
||||
nas_upval(): onstk(true), size(0), stk(nullptr) {}
|
||||
nas_upval(): on_stack(true), size(0), stk(nullptr) {}
|
||||
|
||||
var& operator[](usize n) {
|
||||
return onstk? stk[n]:elems[n];
|
||||
return on_stack? stk[n]:elems[n];
|
||||
}
|
||||
|
||||
void clear() {
|
||||
onstk = true;
|
||||
on_stack = true;
|
||||
elems.clear();
|
||||
size = 0;
|
||||
}
|
||||
|
@ -184,7 +188,7 @@ public:
|
|||
|
||||
struct nas_ghost {
|
||||
private:
|
||||
using destructor=void (*)(void*);
|
||||
using destructor = void (*)(void*);
|
||||
|
||||
public:
|
||||
std::string type_name;
|
||||
|
|
|
@ -188,10 +188,10 @@ void vm::gstate() {
|
|||
}
|
||||
|
||||
void vm::lstate() {
|
||||
if (!ctx.localr || !ctx.funcr.func().lsize) {
|
||||
if (!ctx.localr || !ctx.funcr.func().local_size) {
|
||||
return;
|
||||
}
|
||||
const u32 lsize = ctx.funcr.func().lsize;
|
||||
const u32 lsize = ctx.funcr.func().local_size;
|
||||
std::clog << "local (0x" << std::hex << reinterpret_cast<u64>(ctx.localr)
|
||||
<< " <+" << static_cast<u64>(ctx.localr-ctx.stack)
|
||||
<< ">)\n" << std::dec;
|
||||
|
@ -228,6 +228,22 @@ void vm::detail() {
|
|||
ustate();
|
||||
}
|
||||
|
||||
std::string vm::report_lack_arguments(u32 argc, const nas_func& func) const {
|
||||
auto result = std::string("lack argument(s): ");
|
||||
std::vector<std::string> argument_list = {};
|
||||
argument_list.resize(func.keys.size());
|
||||
for(const auto& i : func.keys) {
|
||||
argument_list[i.second-1] = i.first;
|
||||
}
|
||||
for(u32 i = argc; i<argument_list.size(); ++i) {
|
||||
result += argument_list[i];
|
||||
if (i!=argument_list.size()-1) {
|
||||
result += ", ";
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void vm::die(const std::string& str) {
|
||||
std::cerr << "[vm] error: " << str << "\n";
|
||||
traceback();
|
||||
|
|
117
src/nasal_vm.h
117
src/nasal_vm.h
|
@ -65,6 +65,7 @@ protected:
|
|||
void lstate();
|
||||
void ustate();
|
||||
void detail();
|
||||
std::string report_lack_arguments(u32, const nas_func&) const;
|
||||
void die(const std::string&);
|
||||
|
||||
/* vm calculation functions*/
|
||||
|
@ -204,7 +205,7 @@ inline void vm::o_repl() {
|
|||
|
||||
inline void vm::o_intl() {
|
||||
ctx.top[0].func().local.resize(imm[ctx.pc], nil);
|
||||
ctx.top[0].func().lsize = imm[ctx.pc];
|
||||
ctx.top[0].func().local_size = imm[ctx.pc];
|
||||
}
|
||||
|
||||
inline void vm::o_loadg() {
|
||||
|
@ -217,7 +218,7 @@ inline void vm::o_loadl() {
|
|||
|
||||
inline void vm::o_loadu() {
|
||||
ctx.funcr.func().upval[(imm[ctx.pc]>>16)&0xffff]
|
||||
.upval()[imm[ctx.pc]&0xffff] = (ctx.top--)[0];
|
||||
.upval()[imm[ctx.pc]&0xffff] = (ctx.top--)[0];
|
||||
}
|
||||
|
||||
inline void vm::o_pnum() {
|
||||
|
@ -252,7 +253,7 @@ inline void vm::o_newf() {
|
|||
(++ctx.top)[0] = ngc.alloc(vm_func);
|
||||
auto& func = ctx.top[0].func();
|
||||
func.entry = imm[ctx.pc];
|
||||
func.psize = 1;
|
||||
func.parameter_size = 1;
|
||||
|
||||
/* this means you create a new function in local scope */
|
||||
if (ctx.localr) {
|
||||
|
@ -260,7 +261,7 @@ inline void vm::o_newf() {
|
|||
// function created in the same local scope shares one closure
|
||||
// so this size & stk setting has no problem
|
||||
var upval = (ctx.upvalr.type==vm_nil)? ngc.alloc(vm_upval):ctx.upvalr;
|
||||
upval.upval().size = ctx.funcr.func().lsize;
|
||||
upval.upval().size = ctx.funcr.func().local_size;
|
||||
upval.upval().stk = ctx.localr;
|
||||
func.upval.push_back(upval);
|
||||
ctx.upvalr = upval;
|
||||
|
@ -275,20 +276,20 @@ inline void vm::o_happ() {
|
|||
inline void vm::o_para() {
|
||||
auto& func = ctx.top[0].func();
|
||||
// func->size has 1 place reserved for "me"
|
||||
func.keys[imm[ctx.pc]] = func.psize;
|
||||
func.local[func.psize++] = var::none();
|
||||
func.keys[cstr[imm[ctx.pc]]] = func.parameter_size;
|
||||
func.local[func.parameter_size++] = var::none();
|
||||
}
|
||||
|
||||
inline void vm::o_deft() {
|
||||
var val = ctx.top[0];
|
||||
auto& func = (--ctx.top)[0].func();
|
||||
// func->size has 1 place reserved for "me"
|
||||
func.keys[imm[ctx.pc]] = func.psize;
|
||||
func.local[func.psize++] = val;
|
||||
func.keys[cstr[imm[ctx.pc]]] = func.parameter_size;
|
||||
func.local[func.parameter_size++] = val;
|
||||
}
|
||||
|
||||
inline void vm::o_dyn() {
|
||||
ctx.top[0].func().dpara = imm[ctx.pc];
|
||||
ctx.top[0].func().dynamic_parameter_index = imm[ctx.pc];
|
||||
}
|
||||
|
||||
inline void vm::o_lnot() {
|
||||
|
@ -349,6 +350,7 @@ inline void vm::o_sub() {op_calc(-);}
|
|||
inline void vm::o_mul() {op_calc(*);}
|
||||
inline void vm::o_div() {op_calc(/);}
|
||||
inline void vm::o_lnk() {
|
||||
// concat two vectors into one
|
||||
if (ctx.top[-1].type==vm_vec && ctx.top[0].type==vm_vec) {
|
||||
ngc.temp = ngc.alloc(vm_vec);
|
||||
for(auto i : ctx.top[-1].vec().elems) {
|
||||
|
@ -362,6 +364,7 @@ inline void vm::o_lnk() {
|
|||
--ctx.top;
|
||||
return;
|
||||
}
|
||||
// concat strings
|
||||
ctx.top[-1] = ngc.newstr(ctx.top[-1].tostr()+ctx.top[0].tostr());
|
||||
--ctx.top;
|
||||
}
|
||||
|
@ -607,7 +610,7 @@ inline void vm::o_callv() {
|
|||
ctx.top[0].func().local[0] = val; // 'me'
|
||||
}
|
||||
} else if (vec.type==vm_str) {
|
||||
auto& str = vec.str();
|
||||
const auto& str = vec.str();
|
||||
i32 num = val.tonum();
|
||||
i32 len = str.length();
|
||||
if (num<-len || num>=len) {
|
||||
|
@ -670,13 +673,13 @@ inline void vm::o_callh() {
|
|||
}
|
||||
|
||||
inline void vm::o_callfv() {
|
||||
u32 argc = imm[ctx.pc]; // arguments counter
|
||||
const u32 argc = imm[ctx.pc]; // arguments counter
|
||||
var* local = ctx.top-argc+1; // arguments begin address
|
||||
if (local[-1].type!=vm_func) {
|
||||
die("must call a function");
|
||||
return;
|
||||
}
|
||||
auto& func = local[-1].func();
|
||||
const auto& func = local[-1].func();
|
||||
|
||||
// swap funcr with local[-1]
|
||||
var tmp = local[-1];
|
||||
|
@ -684,27 +687,29 @@ inline void vm::o_callfv() {
|
|||
ctx.funcr = tmp;
|
||||
|
||||
// top-argc+lsize(local) +1(old pc) +1(old localr) +1(old upvalr)
|
||||
if (ctx.top-argc+func.lsize+3>=ctx.canary) {
|
||||
if (ctx.top-argc+func.local_size+3>=ctx.canary) {
|
||||
die("stack overflow");
|
||||
return;
|
||||
}
|
||||
// parameter size is func->psize-1, 1 is reserved for "me"
|
||||
u32 psize = func.psize-1;
|
||||
if (argc<psize && func.local[argc+1].type==vm_none) {
|
||||
die("lack argument(s)");
|
||||
const u32 parameter_size = func.parameter_size-1;
|
||||
if (argc<parameter_size && func.local[argc+1].type==vm_none) {
|
||||
die(report_lack_arguments(argc, func));
|
||||
return;
|
||||
}
|
||||
|
||||
// load dynamic argument, default nil, for better performance
|
||||
var dynamic = nil;
|
||||
if (func.dpara>=0) { // load dynamic arguments
|
||||
if (func.dynamic_parameter_index>=0) {
|
||||
// load dynamic argument
|
||||
dynamic = ngc.alloc(vm_vec);
|
||||
for(u32 i = psize; i<argc; ++i) {
|
||||
for(u32 i = parameter_size; i<argc; ++i) {
|
||||
dynamic.vec().elems.push_back(local[i]);
|
||||
}
|
||||
} else if (psize<argc) {
|
||||
// load arguments to "arg", located at stack+1
|
||||
} else if (parameter_size<argc) {
|
||||
// load arguments to default dynamic argument "arg", located at stack+1
|
||||
dynamic = ngc.alloc(vm_vec);
|
||||
for(u32 i = psize; i<argc; ++i) {
|
||||
for(u32 i = parameter_size; i<argc; ++i) {
|
||||
dynamic.vec().elems.push_back(local[i]);
|
||||
}
|
||||
}
|
||||
|
@ -713,19 +718,21 @@ inline void vm::o_callfv() {
|
|||
// then all the available values the vector needs
|
||||
// are all outside the stack top and may be
|
||||
// collected incorrectly
|
||||
ctx.top = local+func.lsize;
|
||||
ctx.top = local+func.local_size;
|
||||
|
||||
u32 min_size = (std::min)(psize, argc); // avoid error in MSVC
|
||||
const u32 min_size = (std::min)(parameter_size, argc); // avoid error in MSVC
|
||||
for(u32 i = min_size; i>=1; --i) { // load arguments
|
||||
local[i] = local[i-1];
|
||||
}
|
||||
local[0] = func.local[0];// load "me"
|
||||
|
||||
// load local scope & default arguments
|
||||
for(u32 i = min_size+1; i<func.lsize; ++i) {
|
||||
for(u32 i = min_size+1; i<func.local_size; ++i) {
|
||||
local[i] = func.local[i];
|
||||
}
|
||||
local[func.dpara>=0? psize+1:func.lsize-1] = dynamic;
|
||||
// load dynamic argument
|
||||
local[func.dynamic_parameter_index>=0?
|
||||
parameter_size+1:func.local_size-1] = dynamic;
|
||||
|
||||
ctx.top[0] = ctx.upvalr;
|
||||
(++ctx.top)[0] = var::addr(ctx.localr);
|
||||
|
@ -736,41 +743,56 @@ inline void vm::o_callfv() {
|
|||
}
|
||||
|
||||
inline void vm::o_callfh() {
|
||||
auto& hash = ctx.top[0].hash().elems;
|
||||
const auto& hash = ctx.top[0].hash().elems;
|
||||
if (ctx.top[-1].type!=vm_func) {
|
||||
die("must call a function");
|
||||
return;
|
||||
}
|
||||
auto& func = ctx.top[-1].func();
|
||||
const auto& func = ctx.top[-1].func();
|
||||
var tmp = ctx.top[-1];
|
||||
ctx.top[-1] = ctx.funcr;
|
||||
ctx.funcr = tmp;
|
||||
|
||||
// top -1(hash) +lsize(local) +1(old pc) +1(old localr) +1(old upvalr)
|
||||
if (ctx.top+func.lsize+2>= ctx.canary) {
|
||||
if (ctx.top+func.local_size+2>= ctx.canary) {
|
||||
die("stack overflow");
|
||||
return;
|
||||
}
|
||||
if (func.dpara>=0) {
|
||||
die("special call cannot use dynamic argument");
|
||||
// dynamic parameter is not allowed in this kind of function call
|
||||
if (func.dynamic_parameter_index>=0) {
|
||||
die("special call cannot use dynamic argument \"" +
|
||||
cstr[func.dynamic_parameter_index] + "\""
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var* local = ctx.top;
|
||||
ctx.top += func.lsize;
|
||||
for(u32 i = 0; i<func.lsize; ++i) {
|
||||
ctx.top += func.local_size;
|
||||
for(u32 i = 0; i<func.local_size; ++i) {
|
||||
local[i] = func.local[i];
|
||||
}
|
||||
|
||||
bool lack_arguments_flag = false;
|
||||
for(const auto& i : func.keys) {
|
||||
auto& key = cstr[i.first];
|
||||
const auto& key = i.first;
|
||||
if (hash.count(key)) {
|
||||
local[i.second] = hash[key];
|
||||
local[i.second] = hash.at(key);
|
||||
} else if (local[i.second].type==vm_none) {
|
||||
die("lack argument(s): \""+key+"\"");
|
||||
return;
|
||||
lack_arguments_flag = true;
|
||||
}
|
||||
}
|
||||
if (lack_arguments_flag) {
|
||||
auto info = std::string("lack argument(s): ");
|
||||
for(const auto& i : func.keys) {
|
||||
const auto& key = i.first;
|
||||
if (local[i.second].type==vm_none) {
|
||||
info += key + ", ";
|
||||
}
|
||||
}
|
||||
info = info.substr(0, info.length()-2);
|
||||
die(info);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.top[0] = ctx.upvalr;
|
||||
(++ctx.top)[0] = var::addr(ctx.localr);
|
||||
|
@ -831,7 +853,7 @@ inline void vm::o_slc() {
|
|||
inline void vm::o_slc2() {
|
||||
var val2 = (ctx.top--)[0];
|
||||
var val1 = (ctx.top--)[0];
|
||||
auto& ref = ctx.top[-1].vec().elems;
|
||||
const auto& ref = ctx.top[-1].vec().elems;
|
||||
auto& aim = ctx.top[0].vec().elems;
|
||||
|
||||
u8 type1 = val1.type,type2=val2.type;
|
||||
|
@ -875,8 +897,9 @@ inline void vm::o_mcalll() {
|
|||
inline void vm::o_mupval() {
|
||||
ctx.memr = &(
|
||||
ctx.funcr.func()
|
||||
.upval[(imm[ctx.pc]>>16)&0xffff]
|
||||
.upval()[imm[ctx.pc]&0xffff]);
|
||||
.upval[(imm[ctx.pc]>>16)&0xffff]
|
||||
.upval()[imm[ctx.pc]&0xffff]
|
||||
);
|
||||
(++ctx.top)[0] = ctx.memr[0];
|
||||
// push value in this memory space on stack
|
||||
// to avoid being garbage collected
|
||||
|
@ -897,7 +920,7 @@ inline void vm::o_mcallv() {
|
|||
return;
|
||||
}
|
||||
auto& ref = vec.hash();
|
||||
auto& str = val.str();
|
||||
const auto& str = val.str();
|
||||
ctx.memr = ref.get_mem(str);
|
||||
if (!ctx.memr) {
|
||||
ref.elems[str] = nil;
|
||||
|
@ -909,7 +932,7 @@ inline void vm::o_mcallv() {
|
|||
return;
|
||||
}
|
||||
auto& ref = vec.map();
|
||||
auto& str = val.str();
|
||||
const auto& str = val.str();
|
||||
ctx.memr = ref.get_mem(str);
|
||||
if (!ctx.memr) {
|
||||
die("cannot find symbol \"" + str + "\"");
|
||||
|
@ -926,7 +949,7 @@ inline void vm::o_mcallh() {
|
|||
die("must call a hash");
|
||||
return;
|
||||
}
|
||||
auto& str = cstr[imm[ctx.pc]];
|
||||
const auto& str = cstr[imm[ctx.pc]];
|
||||
if (hash.type==vm_map) {
|
||||
ctx.memr = hash.map().get_mem(str);
|
||||
if (!ctx.memr) {
|
||||
|
@ -936,7 +959,8 @@ inline void vm::o_mcallh() {
|
|||
}
|
||||
auto& ref = hash.hash();
|
||||
ctx.memr = ref.get_mem(str);
|
||||
if (!ctx.memr) { // create a new key
|
||||
// create a new key
|
||||
if (!ctx.memr) {
|
||||
ref.elems[str] = nil;
|
||||
ctx.memr = ref.get_mem(str);
|
||||
}
|
||||
|
@ -970,10 +994,11 @@ inline void vm::o_ret() {
|
|||
ctx.funcr = ctx.top[0];
|
||||
ctx.top[0] = ret; // rewrite func with returned value
|
||||
|
||||
if (up.type==vm_upval) { // synchronize upvalue
|
||||
// synchronize upvalue
|
||||
if (up.type==vm_upval) {
|
||||
auto& upval = up.upval();
|
||||
auto size = func.func().lsize;
|
||||
upval.onstk = false;
|
||||
auto size = func.func().local_size;
|
||||
upval.on_stack = false;
|
||||
upval.elems.resize(size);
|
||||
for(u32 i = 0; i<size; ++i) {
|
||||
upval.elems[i] = local[i];
|
||||
|
|
Loading…
Reference in New Issue