diff --git a/readthedocs/ravi-reference.rst b/readthedocs/ravi-reference.rst index e1ed2fbd..0152e276 100644 --- a/readthedocs/ravi-reference.rst +++ b/readthedocs/ravi-reference.rst @@ -38,6 +38,8 @@ The supported type-annotations are as follows: denotes a Lua table ``string`` denotes a string +``boolean`` + denotes a boolean ``closure`` denotes a function ``Name [. Name]*`` diff --git a/src/lcode.c b/src/lcode.c index a9433581..4c89cc45 100644 --- a/src/lcode.c +++ b/src/lcode.c @@ -702,6 +702,8 @@ static void discharge2reg (FuncState *fs, expdesc *e, int reg) { luaK_codeABC(fs, OP_MOVE, reg, e->u.info, 0); if (ravi_type == RAVI_TM_STRING_OR_NIL) luaK_codeABC(fs, OP_RAVI_TOSTRING, reg, 0, 0); + else if (ravi_type == RAVI_TM_BOOLEAN_OR_NIL) + luaK_codeABC(fs, OP_RAVI_TOBOOLEAN, reg, 0, 0); else if (ravi_type == RAVI_TM_FUNCTION_OR_NIL) luaK_codeABC(fs, OP_RAVI_TOCLOSURE, reg, 0, 0); else if (ravi_type == RAVI_TM_USERDATA_OR_NIL && usertype) @@ -870,6 +872,7 @@ static void check_valid_store(FuncState *fs, expdesc *var, expdesc *ex) { var->ravi_type_map == RAVI_TM_INTEGER_ARRAY || var->ravi_type_map == RAVI_TM_TABLE || var->ravi_type_map == RAVI_TM_STRING_OR_NIL || + var->ravi_type_map == RAVI_TM_BOOLEAN_OR_NIL || var->ravi_type_map == RAVI_TM_FUNCTION_OR_NIL || var->ravi_type_map == RAVI_TM_USERDATA_OR_NIL)) { /* handled by MOVEI, MOVEF, MOVEIARRAY, MOVEFARRAY at runtime */ @@ -922,6 +925,15 @@ static void check_valid_store(FuncState *fs, expdesc *var, expdesc *ex) { fs->ls->L, "Invalid assignment: string expected")); } + else if (var->ravi_type_map == RAVI_TM_BOOLEAN_OR_NIL) { + if ((ex_ravi_type_map & ~RAVI_TM_BOOLEAN_OR_NIL) == 0) + return; + luaX_syntaxerror( + fs->ls, + luaO_pushfstring( + fs->ls->L, + "Invalid assignment: boolean expected")); + } else if (var->ravi_type_map == RAVI_TM_FUNCTION_OR_NIL) { if ((ex_ravi_type_map & ~RAVI_TM_FUNCTION_OR_NIL) == 0) return; @@ -948,7 +960,7 @@ static OpCode check_valid_setupval(FuncState *fs, expdesc *var, expdesc *ex, OpCode op = OP_SETUPVAL; if ((var->ravi_type_map == RAVI_TM_INTEGER || var->ravi_type_map == RAVI_TM_FLOAT || var->ravi_type_map == RAVI_TM_INTEGER_ARRAY || var->ravi_type_map == RAVI_TM_FLOAT_ARRAY || - var->ravi_type_map == RAVI_TM_TABLE || var->ravi_type_map == RAVI_TM_STRING_OR_NIL || + var->ravi_type_map == RAVI_TM_TABLE || var->ravi_type_map == RAVI_TM_STRING_OR_NIL || var->ravi_type_map == RAVI_TM_BOOLEAN_OR_NIL || var->ravi_type_map == RAVI_TM_FUNCTION_OR_NIL || var->ravi_type_map == RAVI_TM_USERDATA_OR_NIL) && ex->ravi_type_map & ~var->ravi_type_map) { if (var->ravi_type_map == RAVI_TM_INTEGER) @@ -963,6 +975,8 @@ static OpCode check_valid_setupval(FuncState *fs, expdesc *var, expdesc *ex, op = OP_RAVI_SETUPVALT; else if (var->ravi_type_map == RAVI_TM_STRING_OR_NIL) luaK_codeABC(fs, OP_RAVI_TOSTRING, reg, 0, 0); + else if (var->ravi_type_map == RAVI_TM_BOOLEAN_OR_NIL) + luaK_codeABC(fs, OP_RAVI_TOBOOLEAN, reg, 0, 0); else if (var->ravi_type_map == RAVI_TM_FUNCTION_OR_NIL) luaK_codeABC(fs, OP_RAVI_TOCLOSURE, reg, 0, 0); else if (var->ravi_type_map == RAVI_TM_USERDATA_OR_NIL) { @@ -1517,6 +1531,10 @@ static void code_type_assertion(FuncState *fs, UnOpr op, expdesc *e, TString *us opcode = OP_RAVI_TOINT; tm = RAVI_TM_INTEGER; } + else if (op == OPR_TO_BOOLEAN && e->ravi_type_map != RAVI_TM_BOOLEAN) { + opcode = OP_RAVI_TOBOOLEAN; + tm = RAVI_TM_BOOLEAN; + } else if (op == OPR_TO_INTARRAY && e->ravi_type_map != RAVI_TM_INTEGER_ARRAY) { if (e->ravi_type_map == RAVI_TM_TABLE && e->pc >= 0) { Instruction *i = &fs->f->code[e->pc]; @@ -1551,6 +1569,10 @@ static void code_type_assertion(FuncState *fs, UnOpr op, expdesc *e, TString *us opcode = OP_RAVI_TOSTRING; tm = RAVI_TM_STRING_OR_NIL; } + else if (op == OPR_TO_BOOLEAN && (e->ravi_type_map & (~RAVI_TM_BOOLEAN_OR_NIL)) != 0) { + opcode = OP_RAVI_TOBOOLEAN; + tm = RAVI_TM_BOOLEAN_OR_NIL; + } else if (op == OPR_TO_CLOSURE && (e->ravi_type_map & (~RAVI_TM_FUNCTION_OR_NIL)) != 0) { opcode = OP_RAVI_TOCLOSURE; tm = RAVI_TM_FUNCTION_OR_NIL; @@ -1598,7 +1620,7 @@ void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line, TString *userty case OPR_LEN: codeunexpval(fs, cast(OpCode, op + OP_UNM), e, line); break; - case OPR_TO_INTEGER: case OPR_TO_NUMBER: case OPR_TO_INTARRAY: + case OPR_TO_INTEGER: case OPR_TO_NUMBER: case OPR_TO_INTARRAY: case OPR_TO_BOOLEAN: case OPR_TO_NUMARRAY: case OPR_TO_TABLE: case OPR_TO_STRING: case OPR_TO_CLOSURE: code_type_assertion(fs, op, e, NULL); break; case OPR_TO_TYPE: diff --git a/src/lcode.h b/src/lcode.h index 86743191..e428b1c7 100644 --- a/src/lcode.h +++ b/src/lcode.h @@ -39,7 +39,7 @@ typedef enum BinOpr { /** RAVI change */ typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_TO_INTEGER, OPR_TO_NUMBER, OPR_TO_INTARRAY, OPR_TO_NUMARRAY, OPR_TO_TABLE, OPR_TO_STRING, - OPR_TO_CLOSURE, OPR_TO_TYPE, OPR_NOUNOPR } UnOpr; + OPR_TO_BOOLEAN, OPR_TO_CLOSURE, OPR_TO_TYPE, OPR_NOUNOPR } UnOpr; /* get (pointer to) instruction of given 'expdesc' */ #define getinstruction(fs,e) ((fs)->f->code[(e)->u.info]) diff --git a/src/llex.c b/src/llex.c index 84eec88d..6302068c 100644 --- a/src/llex.c +++ b/src/llex.c @@ -50,7 +50,7 @@ static const char *const luaX_tokens [] = { "<<", ">>", "::", "", "", "", "", "", "@integer", "@number", "@integer[]", "@number[]", - "@table", "@string", "@closure" + "@table", "@string", "@boolean", "@closure" }; @@ -478,6 +478,8 @@ static int casttoken(LexState *ls, SemInfo *seminfo) { tok = TK_TO_TABLE; else if (strncmp(s, "@string", n) == 0) tok = TK_TO_STRING; + else if (strncmp(s, "@boolean", n) == 0) + tok = TK_TO_BOOLEAN; else if (strncmp(s, "@closure", n) == 0) tok = TK_TO_CLOSURE; else { diff --git a/src/llex.h b/src/llex.h index 6b66fa9b..98104351 100644 --- a/src/llex.h +++ b/src/llex.h @@ -40,7 +40,7 @@ enum RESERVED { TK_FLT, TK_INT, TK_NAME, TK_STRING, /** RAVI extensions */ TK_TO_INTEGER, TK_TO_NUMBER, TK_TO_INTARRAY, TK_TO_NUMARRAY, - TK_TO_TABLE, TK_TO_STRING, TK_TO_CLOSURE + TK_TO_TABLE, TK_TO_STRING, TK_TO_BOOLEAN, TK_TO_CLOSURE }; /* number of reserved words */ diff --git a/src/lopcodes.c b/src/lopcodes.c index ad560b33..b2a8a2d5 100644 --- a/src/lopcodes.c +++ b/src/lopcodes.c @@ -112,6 +112,7 @@ LUAI_DDEF const char *const luaP_opnames[NUM_OPCODES+1] = { "TOFARRAY", /* A R(A) := to_arrayf(R(A)) */ "TOTAB", /* A R(A) := to_table(R(A)) */ "TOSTRING", + "TOBOOLEAN", "TOCLOSURE", "TOTYPE", @@ -256,6 +257,7 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { ,opmode(0, 1, OpArgN, OpArgN, iABC) /* OP_RAVI_TOFARRAY A R(A) := check_array_of_float(R(A)) */ ,opmode(0, 1, OpArgN, OpArgN, iABC) /* OP_RAVI_TOTAB A R(A) := check_table(R(A)) */ ,opmode(0, 1, OpArgN, OpArgN, iABC) /* OP_RAVI_TOSTRING */ + ,opmode(0, 1, OpArgN, OpArgN, iABC) /* OP_RAVI_TOBOOLEAN */ ,opmode(0, 1, OpArgN, OpArgN, iABC) /* OP_RAVI_TOCLOSURE */ ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_RAVI_TOTYPE */ diff --git a/src/lopcodes.h b/src/lopcodes.h index e5381eb4..0f277070 100644 --- a/src/lopcodes.h +++ b/src/lopcodes.h @@ -223,6 +223,7 @@ OP_RAVI_TOIARRAY, /* A R(A) := to_arrayi(R(A)) */ OP_RAVI_TOFARRAY, /* A R(A) := to_arrayf(R(A)) */ OP_RAVI_TOTAB, /* A R(A) := to_table(R(A)) */ OP_RAVI_TOSTRING, /* A R(A) := assert_string(R(A)) */ +OP_RAVI_TOBOOLEAN, /* A R(A) := assert_string(R(A)) */ OP_RAVI_TOCLOSURE, /* A R(A) := assert_closure(R(A)) */ OP_RAVI_TOTYPE, /* A R(A) := assert_usertype(R(A)), where usertype has metatable in Lua registry */ diff --git a/src/lparser.c b/src/lparser.c index ee3d7f2e..c60bb352 100644 --- a/src/lparser.c +++ b/src/lparser.c @@ -639,9 +639,10 @@ static void ravi_code_typecoersion(LexState *ls, int reg, ravi_type_map tm, luaK_codeABx(ls->fs, OP_RAVI_TOTYPE, reg, luaK_stringK(ls->fs, typename)); else if (tm == RAVI_TM_STRING_OR_NIL) luaK_codeABC(ls->fs, OP_RAVI_TOSTRING, reg, 0, 0); + else if (tm == RAVI_TM_BOOLEAN_OR_NIL) + luaK_codeABC(ls->fs, OP_RAVI_TOBOOLEAN, reg, 0, 0); else if (tm == RAVI_TM_FUNCTION_OR_NIL) luaK_codeABC(ls->fs, OP_RAVI_TOCLOSURE, reg, 0, 0); - // TODO coerse to boolean } /* RAVI code an instruction to initialize a scalar typed value @@ -1279,8 +1280,8 @@ static ravi_type_map declare_localvar(LexState *ls, TString **pusertype) { tm = RAVI_TM_TABLE; else if (strcmp(str, "string") == 0) tm = RAVI_TM_STRING_OR_NIL; - //else if (strcmp(str, "boolean") == 0) - // tm = RAVI_TM_BOOLEAN_OR_NIL; + else if (strcmp(str, "boolean") == 0) + tm = RAVI_TM_BOOLEAN_OR_NIL; else if (strcmp(str, "any") == 0) tm = RAVI_TM_ANY; else { @@ -1458,7 +1459,7 @@ static void ravi_typecheck(LexState *ls, expdesc *v, ravi_type_map *var_types, /* if we are calling a function then convert return types */ else if ((vartype == RAVI_TM_FLOAT || vartype == RAVI_TM_INTEGER || vartype == RAVI_TM_FLOAT_ARRAY || vartype == RAVI_TM_INTEGER_ARRAY || - vartype == RAVI_TM_TABLE || vartype == RAVI_TM_STRING_OR_NIL || + vartype == RAVI_TM_TABLE || vartype == RAVI_TM_STRING_OR_NIL || vartype == RAVI_TM_BOOLEAN_OR_NIL || vartype == RAVI_TM_FUNCTION_OR_NIL || vartype == RAVI_TM_USERDATA_OR_NIL) && v->k == VCALL) { /* For local variable declarations that call functions e.g. @@ -1488,7 +1489,8 @@ static void ravi_typecheck(LexState *ls, expdesc *v, ravi_type_map *var_types, } else if (vartype == RAVI_TM_STRING_OR_NIL || vartype == RAVI_TM_FUNCTION_OR_NIL || - vartype == RAVI_TM_USERDATA_OR_NIL) { + vartype == RAVI_TM_USERDATA_OR_NIL || + vartype == RAVI_TM_BOOLEAN_OR_NIL) { TString *usertype = usertypes[n]; // NULL if var_types[n] is not userdata /* we need to make sure that a register is assigned to 'v' so that we can emit type assertion instructions. This would have @@ -1707,6 +1709,7 @@ static UnOpr getunopr (int op) { case TK_TO_NUMARRAY: return OPR_TO_NUMARRAY; case TK_TO_TABLE: return OPR_TO_TABLE; case TK_TO_STRING: return OPR_TO_STRING; + case TK_TO_BOOLEAN: return OPR_TO_BOOLEAN; case TK_TO_CLOSURE: return OPR_TO_CLOSURE; case '@': return OPR_TO_TYPE; default: return OPR_NOUNOPR; diff --git a/src/lvm.c b/src/lvm.c index 57dd0208..dda99427 100644 --- a/src/lvm.c +++ b/src/lvm.c @@ -79,7 +79,6 @@ #endif - /* ** Try to convert a value from string to a number value. ** If the value is not a string or is a string not representing @@ -95,6 +94,33 @@ static int l_strton (const TValue *obj, TValue *result) { return (luaO_str2num(svalue(obj), result) == vslen(obj) + 1); } +/* +** Try to convert a value (string or numeric) to boolean. +** On successful conversion returns 1 and stores value in (*b). +** On failure, returns 0. +*/ +int luaV_toboolean (const TValue *obj, int *b) { + // initial value is negative + (*b) = -1; + // try as integer + if (ttisinteger(obj)) { + int i = ivalue(obj); + if (i == 0) + (*b) = 0; + else if (i == 1) + (*b) = 1; + } + // try as string + else if (ttisstring(obj)) { + const char* s = getstr(tsvalue(obj)); + if (strcmp(s, "false") == 0) + (*b) = 0; + else if (strcmp(s, "true") == 0) + (*b) = 1; + } + // if still negative (so unchanged) -- conversion failed + return ((*b) >= 0); +} /* ** Try to convert a value to a float. The float case is already handled @@ -1331,6 +1357,7 @@ int luaV_execute (lua_State *L) { &&vmlabel(OP_RAVI_TOFARRAY), &&vmlabel(OP_RAVI_TOTAB), &&vmlabel(OP_RAVI_TOSTRING), + &&vmlabel(OP_RAVI_TOBOOLEAN), &&vmlabel(OP_RAVI_TOCLOSURE), &&vmlabel(OP_RAVI_TOTYPE), &&vmlabel(OP_RAVI_MOVEI), @@ -2589,6 +2616,13 @@ int luaV_execute (lua_State *L) { luaG_runerror(L, "string expected"); vmbreak; } + vmcase(OP_RAVI_TOBOOLEAN) { + int b; + if (RAVI_LIKELY(luaV_toboolean(ra, &b))) { setbvalue(ra, b); } + if (!ttisnil(ra) && RAVI_UNLIKELY(!ttisboolean(ra))) + luaG_runerror(L, "boolean expected"); + vmbreak; + } vmcase(OP_RAVI_TOCLOSURE) { if (!ttisnil(ra) && RAVI_UNLIKELY(!ttisclosure(ra))) luaG_runerror(L, "closure expected"); diff --git a/src/lvm.h b/src/lvm.h index 2d66c5a2..7703d318 100644 --- a/src/lvm.h +++ b/src/lvm.h @@ -109,7 +109,7 @@ typedef enum { - +LUAI_FUNC int luaV_toboolean (const TValue *obj, int *b); LUAI_FUNC int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2); LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r); LUAI_FUNC int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r); diff --git a/src/ravi_jitshared.c b/src/ravi_jitshared.c index d9407104..b4320bea 100644 --- a/src/ravi_jitshared.c +++ b/src/ravi_jitshared.c @@ -600,6 +600,7 @@ static const char Lua_header[] = " (ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o,i,LUA_FLOORN2I))\n" "extern int luaV_tonumber_(const TValue *obj, lua_Number *n);\n" "extern int luaV_tointeger(const TValue *obj, lua_Integer *p, int mode);\n" + "extern int luaV_toboolean (const TValue *obj, int *b); \n" #ifdef RAVI_DEFER_STATEMENT "extern int luaF_close (lua_State *L, StkId level, int status);\n" #else @@ -1659,6 +1660,21 @@ static void emit_op_tostring(struct function *fn, int A, int pc) { membuff_add_string(&fn->body, "}\n"); } +static void emit_op_toboolean(struct function *fn, int A, int pc) { + (void)pc; + emit_reg(fn, "ra", A); + membuff_add_string(&fn->body, "int bool = 0;\n"); + membuff_add_string(&fn->body, "if (luaV_toboolean(ra, &bool)) { setbvalue(ra, bool); }\n"); + membuff_add_string(&fn->body, "if (!ttisboolean(ra)) {\n"); +#if GOTO_ON_ERROR + membuff_add_fstring(&fn->body, " error_code = %d;\n", Error_boolean_expected); + membuff_add_string(&fn->body, " goto Lraise_error;\n"); +#else + membuff_add_fstring(&fn->body, " raviV_raise_error(L, %d);\n", Error_boolean_expected); +#endif + membuff_add_string(&fn->body, "}\n"); +} + static void emit_op_totype(struct function *fn, int A, int Bx, int pc) { (void)pc; emit_reg(fn, "ra", A); @@ -2267,6 +2283,9 @@ bool raviJ_codegen(struct lua_State *L, struct Proto *p, struct ravi_compile_opt case OP_RAVI_TOSTRING: { emit_op_tostring(&fn, A, pc); } break; + case OP_RAVI_TOBOOLEAN: { + emit_op_toboolean(&fn, A, pc); + } break; case OP_RAVI_TOCLOSURE: { emit_op_toclosure(&fn, A, pc); } break; @@ -2391,6 +2410,7 @@ static const char *errortext[] = {"integer expected", "for initial value must be a number", "array index is out of bounds", "string expected", + "boolean expected", "closure expected", "type mismatch: wrong userdata type", NULL}; @@ -2404,3 +2424,4 @@ void raviV_raise_error_with_info(lua_State *L, int errorcode, const char *info) assert(errorcode == Error_type_mismatch); luaG_runerror(L, "type mismatch: expected %s", info); } + diff --git a/src/ravi_jitshared.h b/src/ravi_jitshared.h index 4dd654f0..075b90a5 100644 --- a/src/ravi_jitshared.h +++ b/src/ravi_jitshared.h @@ -90,6 +90,7 @@ enum errorcode { Error_for_initial_value_must_be_number, Error_array_out_of_bounds, Error_string_expected, + Error_boolean_expected, Error_closure_expected, Error_type_mismatch, }; diff --git a/src/ravi_mirjit.c b/src/ravi_mirjit.c index dc7baecd..3059b7cd 100644 --- a/src/ravi_mirjit.c +++ b/src/ravi_mirjit.c @@ -151,6 +151,7 @@ static LuaFunc Lua_functions[] = { { "luaF_newproto", luaF_newproto }, { "luaD_inctop", luaD_inctop }, { "luaM_realloc_", luaM_realloc_ }, + { "luaV_toboolean", luaV_toboolean}, { NULL, NULL } }; diff --git a/tests/language/ravi_errors.ravi b/tests/language/ravi_errors.ravi index a35680d6..a5cd2376 100644 --- a/tests/language/ravi_errors.ravi +++ b/tests/language/ravi_errors.ravi @@ -101,6 +101,7 @@ checkmessage('@string 1', 'unexpected symbol near @string') -- FIXME bad error m checkmessage('@MyType 1', "unexpected symbol near '@'") -- FIXME bad error message checkmessage('local function x(y: MyType) end x(1)', 'type mismatch: expected MyType') checkmessage('local function x(y: string) end x(1)', 'string expected') +checkmessage('local function x(y: boolean) end x(1)', 'boolean expected') checkmessage('local function x(y: closure) end x(1)', 'closure expected') checkmessage('local function x() local s: string; s = 1 end', "Invalid assignment: string expected near 'end'") checkmessage('function x() local s: string; s = (function() return 1 end)() end; x()', 'string expected') @@ -109,4 +110,4 @@ checkmessage('table.intarray(0xFFFFFFFF)', 'array length out of range') -- Note checkmessage('table.numarray(0xFFFFFFFF)', 'array length out of range') -- Note only valid when int is 32 bits checkmessage('table.slice(table.intarray(3), 2, 0xFFFFFFFE)', 'cannot create a slice of given bounds') -print 'Ok' \ No newline at end of file +print 'Ok'