Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revive boolean type #227

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions readthedocs/ravi-reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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]*``
Expand Down
26 changes: 24 additions & 2 deletions src/lcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Expand All @@ -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)
Expand All @@ -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) {
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion src/lcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down
4 changes: 3 additions & 1 deletion src/llex.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ static const char *const luaX_tokens [] = {
"<<", ">>", "::", "<eof>",
"<number>", "<integer>", "<name>", "<string>",
"@integer", "@number", "@integer[]", "@number[]",
"@table", "@string", "@closure"
"@table", "@string", "@boolean", "@closure"
};


Expand Down Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion src/llex.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
2 changes: 2 additions & 0 deletions src/lopcodes.c
Original file line number Diff line number Diff line change
Expand Up @@ -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",

Expand Down Expand Up @@ -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 */

Expand Down
1 change: 1 addition & 0 deletions src/lopcodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */

Expand Down
13 changes: 8 additions & 5 deletions src/lparser.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
36 changes: 35 additions & 1 deletion src/lvm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't comply with Lua semantics - string and numbers are true.

// 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
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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); }
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not convinced we should conversion.

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");
Expand Down
2 changes: 1 addition & 1 deletion src/lvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
21 changes: 21 additions & 0 deletions src/ravi_jitshared.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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");
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, not convinced conversion is a good idea.

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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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};
Expand All @@ -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);
}

1 change: 1 addition & 0 deletions src/ravi_jitshared.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
1 change: 1 addition & 0 deletions src/ravi_mirjit.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
};

Expand Down
3 changes: 2 additions & 1 deletion tests/language/ravi_errors.ravi
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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'
print 'Ok'