Made Booleans a first-class type. (0.23.0)
This commit is contained in:
parent
bd51aea318
commit
95eba1eb12
12 changed files with 131 additions and 18 deletions
36
src/parser.c
36
src/parser.c
|
|
@ -432,7 +432,7 @@ static int emit_primary(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
|||
char *name = NULL;
|
||||
if (read_identifier_into(src, len, pos, &name)) {
|
||||
if (strcmp(name, "true") == 0 || strcmp(name, "false") == 0) {
|
||||
int ci = bytecode_add_constant(bc, make_int(strcmp(name, "true") == 0 ? 1 : 0));
|
||||
int ci = bytecode_add_constant(bc, make_bool(strcmp(name, "true") == 0 ? 1 : 0));
|
||||
free(name);
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, ci);
|
||||
return 1;
|
||||
|
|
@ -1775,7 +1775,7 @@ static int emit_and_expr(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
|||
}
|
||||
|
||||
/* all were truthy -> result true */
|
||||
int c1 = bytecode_add_constant(bc, make_int(1));
|
||||
int c1 = bytecode_add_constant(bc, make_bool(1));
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
int j_end = bytecode_add_instruction(bc, OP_JUMP, 0);
|
||||
|
||||
|
|
@ -1784,7 +1784,7 @@ static int emit_and_expr(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
|||
for (int i = 0; i < jf_count; ++i) {
|
||||
bytecode_set_operand(bc, jf_idxs[i], l_false);
|
||||
}
|
||||
int c0 = bytecode_add_constant(bc, make_int(0));
|
||||
int c0 = bytecode_add_constant(bc, make_bool(0));
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c0);
|
||||
|
||||
/* end */
|
||||
|
|
@ -1813,8 +1813,8 @@ static int emit_or_expr(Bytecode *bc, const char *src, size_t len, size_t *pos)
|
|||
/* if current value is false -> proceed to next; else -> result true */
|
||||
int jf_proceed = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
|
||||
|
||||
/* true path: push 1 and jump to end */
|
||||
int c1 = bytecode_add_constant(bc, make_int(1));
|
||||
/* true path: push true and jump to end */
|
||||
int c1 = bytecode_add_constant(bc, make_bool(1));
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, c1);
|
||||
if (tj_count < (int)(sizeof(true_jumps) / sizeof(true_jumps[0]))) {
|
||||
true_jumps[tj_count++] = bytecode_add_instruction(bc, OP_JUMP, 0);
|
||||
|
|
@ -2256,7 +2256,18 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
|||
/* Continue after checks */
|
||||
bytecode_set_operand(bc, j_ok, bc->instr_count);
|
||||
} else if (decl_meta == TYPE_META_BOOLEAN) {
|
||||
/* expect Number then clamp to 1 bit (unsigned) */
|
||||
/* accept Boolean literal or Number; if Number, clamp to 0/1 */
|
||||
/* check if value is Boolean */
|
||||
bytecode_add_instruction(bc, OP_DUP, 0);
|
||||
bytecode_add_instruction(bc, OP_TYPEOF, 0);
|
||||
int ciBool = bytecode_add_constant(bc, make_string("Boolean"));
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, ciBool);
|
||||
bytecode_add_instruction(bc, OP_EQ, 0);
|
||||
int j_not_bool = bytecode_add_instruction(bc, OP_JUMP_IF_FALSE, 0);
|
||||
/* it is Boolean -> OK, skip number check */
|
||||
int j_done = bytecode_add_instruction(bc, OP_JUMP, 0);
|
||||
/* not Boolean: check Number */
|
||||
bytecode_set_operand(bc, j_not_bool, bc->instr_count);
|
||||
bytecode_add_instruction(bc, OP_DUP, 0);
|
||||
bytecode_add_instruction(bc, OP_TYPEOF, 0);
|
||||
int ciNum = bytecode_add_constant(bc, make_string("Number"));
|
||||
|
|
@ -2266,14 +2277,16 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
|||
int j_skip_err = bytecode_add_instruction(bc, OP_JUMP, 0);
|
||||
bytecode_set_operand(bc, j_to_error, bc->instr_count);
|
||||
{
|
||||
int ciMsg = bytecode_add_constant(bc, make_string("TypeError: expected Number for boolean"));
|
||||
int ciMsg = bytecode_add_constant(bc, make_string("TypeError: expected Boolean or Number for boolean"));
|
||||
bytecode_add_instruction(bc, OP_LOAD_CONST, ciMsg);
|
||||
bytecode_add_instruction(bc, OP_PRINT, 0);
|
||||
bytecode_add_instruction(bc, OP_HALT, 0);
|
||||
}
|
||||
bytecode_set_operand(bc, j_skip_err, bc->instr_count);
|
||||
/* clamp to 0/1 */
|
||||
/* if Number, clamp to 0/1 */
|
||||
bytecode_add_instruction(bc, OP_UCLAMP, 1);
|
||||
/* common continuation */
|
||||
bytecode_set_operand(bc, j_done, bc->instr_count);
|
||||
} else if (decl_meta == TYPE_META_NIL) {
|
||||
/* expect Nil */
|
||||
bytecode_add_instruction(bc, OP_DUP, 0);
|
||||
|
|
@ -2331,8 +2344,11 @@ static void parse_simple_statement(Bytecode *bc, const char *src, size_t len, si
|
|||
} else if (is_class_tkn) {
|
||||
/* Class-typed variable defaults to Nil until assigned an instance */
|
||||
ci = bytecode_add_constant(bc, make_nil());
|
||||
} else if (is_number || is_boolean || (decl_bits != 0)) {
|
||||
/* integers/booleans default to 0 */
|
||||
} else if (is_boolean) {
|
||||
/* booleans default to false */
|
||||
ci = bytecode_add_constant(bc, make_bool(0));
|
||||
} else if (is_number || (decl_bits != 0)) {
|
||||
/* integers default to 0 */
|
||||
ci = bytecode_add_constant(bc, make_int(0));
|
||||
}
|
||||
if (ci >= 0) {
|
||||
|
|
|
|||
20
src/value.c
20
src/value.c
|
|
@ -37,6 +37,13 @@ Value make_int(int64_t v) {
|
|||
return val;
|
||||
}
|
||||
|
||||
Value make_bool(int v) {
|
||||
Value val;
|
||||
val.type = VAL_BOOL;
|
||||
val.i = v ? 1 : 0;
|
||||
return val;
|
||||
}
|
||||
|
||||
Value make_string(const char *s) {
|
||||
Value val;
|
||||
val.type = VAL_STRING;
|
||||
|
|
@ -222,6 +229,9 @@ Value copy_value(const Value *v) {
|
|||
case VAL_INT:
|
||||
out.i = v->i;
|
||||
break;
|
||||
case VAL_BOOL:
|
||||
out.i = v->i ? 1 : 0;
|
||||
break;
|
||||
case VAL_STRING:
|
||||
out.s = v->s ? strdup(v->s) : strdup("");
|
||||
break;
|
||||
|
|
@ -252,6 +262,8 @@ Value deep_copy_value(const Value *v) {
|
|||
switch (v->type) {
|
||||
case VAL_INT:
|
||||
return make_int(v->i);
|
||||
case VAL_BOOL:
|
||||
return make_bool(v->i);
|
||||
case VAL_STRING:
|
||||
return make_string(v->s ? v->s : "");
|
||||
case VAL_FUNCTION:
|
||||
|
|
@ -325,6 +337,9 @@ void print_value(const Value *v) {
|
|||
case VAL_STRING:
|
||||
printf("%s", v->s ? v->s : "");
|
||||
break;
|
||||
case VAL_BOOL:
|
||||
printf("%s", v->i ? "true" : "false");
|
||||
break;
|
||||
case VAL_FUNCTION:
|
||||
printf("<function@%p>", (void*)v->fn);
|
||||
break;
|
||||
|
|
@ -364,6 +379,8 @@ int value_is_truthy(const Value *v) {
|
|||
switch (v->type) {
|
||||
case VAL_INT:
|
||||
return v->i != 0;
|
||||
case VAL_BOOL:
|
||||
return v->i != 0;
|
||||
case VAL_STRING:
|
||||
return v->s && v->s[0] != '\0';
|
||||
case VAL_FUNCTION:
|
||||
|
|
@ -390,6 +407,8 @@ char *value_to_string_alloc(const Value *v) {
|
|||
}
|
||||
case VAL_STRING:
|
||||
return strdup(v->s ? v->s : "");
|
||||
case VAL_BOOL:
|
||||
return strdup(v->i ? "true" : "false");
|
||||
case VAL_FUNCTION: {
|
||||
snprintf(buf, sizeof(buf), "<function@%p>", (void*)v->fn);
|
||||
return strdup(buf);
|
||||
|
|
@ -410,6 +429,7 @@ int value_equals(const Value *a, const Value *b) {
|
|||
if (a->type != b->type) return 0;
|
||||
switch (a->type) {
|
||||
case VAL_INT: return a->i == b->i;
|
||||
case VAL_BOOL: return (a->i != 0) == (b->i != 0);
|
||||
case VAL_STRING: {
|
||||
const char *sa = a->s ? a->s : "";
|
||||
const char *sb = b->s ? b->s : "";
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ struct Map; /* forward */
|
|||
|
||||
typedef enum {
|
||||
VAL_INT,
|
||||
VAL_BOOL,
|
||||
VAL_STRING,
|
||||
VAL_FUNCTION,
|
||||
VAL_ARRAY,
|
||||
|
|
@ -66,6 +67,7 @@ typedef struct {
|
|||
|
||||
/* constructors / helpers */
|
||||
Value make_int(int64_t v);
|
||||
Value make_bool(int v);
|
||||
Value make_string(const char *s);
|
||||
Value make_function(struct Bytecode *fn);
|
||||
Value make_nil(void);
|
||||
|
|
|
|||
|
|
@ -37,6 +37,6 @@ case OP_AND: {
|
|||
int res = value_is_truthy(&a) && value_is_truthy(&b);
|
||||
free_value(a);
|
||||
free_value(b);
|
||||
push_value(vm, make_int(res));
|
||||
push_value(vm, make_bool(res));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,15 +38,23 @@ case OP_EQ: {
|
|||
if (a.type == b.type) {
|
||||
switch (a.type) {
|
||||
case VAL_INT: eq = (a.i == b.i); break;
|
||||
case VAL_BOOL: eq = ((a.i != 0) == (b.i != 0)); break;
|
||||
case VAL_STRING: eq = (a.s && b.s) ? (strcmp(a.s, b.s) == 0) : (a.s == b.s); break;
|
||||
case VAL_FUNCTION: eq = (a.fn == b.fn); break;
|
||||
case VAL_NIL: eq = 1; break;
|
||||
default: eq = 0; break;
|
||||
}
|
||||
} else {
|
||||
eq = 0;
|
||||
/* interop: bool vs int (0/1) */
|
||||
if ((a.type == VAL_BOOL && b.type == VAL_INT) || (a.type == VAL_INT && b.type == VAL_BOOL)) {
|
||||
int ai = (a.type == VAL_BOOL) ? (a.i != 0) : (a.i != 0);
|
||||
int bi = (b.type == VAL_BOOL) ? (b.i != 0) : (b.i != 0);
|
||||
eq = (ai == bi);
|
||||
} else {
|
||||
eq = 0;
|
||||
}
|
||||
}
|
||||
push_value(vm, make_int(eq ? 1 : 0));
|
||||
push_value(vm, make_bool(eq));
|
||||
free_value(a); free_value(b);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,15 +38,23 @@ case OP_NEQ: {
|
|||
if (a.type == b.type) {
|
||||
switch (a.type) {
|
||||
case VAL_INT: neq = (a.i != b.i); break;
|
||||
case VAL_BOOL: neq = ((a.i != 0) != (b.i != 0)); break;
|
||||
case VAL_STRING: neq = (a.s && b.s) ? (strcmp(a.s, b.s) != 0) : (a.s != b.s); break;
|
||||
case VAL_FUNCTION: neq = (a.fn != b.fn); break;
|
||||
case VAL_NIL: neq = 0; break;
|
||||
default: neq = 1; break;
|
||||
}
|
||||
} else {
|
||||
neq = 1;
|
||||
/* interop: bool vs int (0/1) */
|
||||
if ((a.type == VAL_BOOL && b.type == VAL_INT) || (a.type == VAL_INT && b.type == VAL_BOOL)) {
|
||||
int ai = (a.type == VAL_BOOL) ? (a.i != 0) : (a.i != 0);
|
||||
int bi = (b.type == VAL_BOOL) ? (b.i != 0) : (b.i != 0);
|
||||
neq = (ai != bi);
|
||||
} else {
|
||||
neq = 1;
|
||||
}
|
||||
}
|
||||
push_value(vm, make_int(neq ? 1 : 0));
|
||||
push_value(vm, make_bool(neq));
|
||||
free_value(a); free_value(b);
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,6 @@ case OP_NOT: {
|
|||
Value v = pop_value(vm);
|
||||
int res = !value_is_truthy(&v);
|
||||
free_value(v);
|
||||
push_value(vm, make_int(res));
|
||||
push_value(vm, make_bool(res));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,6 @@ case OP_OR: {
|
|||
int res = value_is_truthy(&a) || value_is_truthy(&b);
|
||||
free_value(a);
|
||||
free_value(b);
|
||||
push_value(vm, make_int(res));
|
||||
push_value(vm, make_bool(res));
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ case OP_TYPEOF: {
|
|||
const char *tname = "Unknown";
|
||||
switch (v.type) {
|
||||
case VAL_INT: tname = "Number"; break;
|
||||
case VAL_BOOL: tname = "Boolean"; break;
|
||||
case VAL_STRING: tname = "String"; break;
|
||||
case VAL_FUNCTION: tname = "Function"; break;
|
||||
case VAL_ARRAY: tname = "Array"; break;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue