Fix up if var := ...:
in ternary expressions and optional checking
logic
This commit is contained in:
parent
2b454d1408
commit
fb37b0ee42
78
compile.c
78
compile.c
@ -2953,30 +2953,72 @@ CORD compile(env_t *env, ast_t *ast)
|
||||
}
|
||||
case If: {
|
||||
auto if_ = Match(ast, If);
|
||||
if (!if_->else_body)
|
||||
code_err(ast, "'if' expressions can only be used if you also have an 'else' block");
|
||||
ast_t *condition = if_->condition;
|
||||
CORD decl_code = CORD_EMPTY;
|
||||
env_t *truthy_scope = env, *falsey_scope = env;
|
||||
|
||||
type_t *t = get_type(env, ast);
|
||||
if (t->tag == VoidType || t->tag == AbortType)
|
||||
code_err(ast, "This expression has a %T type, but it needs to have a real value", t);
|
||||
type_t *condition_type;
|
||||
if (condition->tag == Declare) {
|
||||
condition_type = get_type(env, Match(condition, Declare)->value);
|
||||
|
||||
CORD condition;
|
||||
if (get_type(env, if_->condition)->tag == TextType)
|
||||
condition = CORD_all("(", compile(env, if_->condition), ").length");
|
||||
else
|
||||
condition = compile(env, if_->condition);
|
||||
const char *varname = Match(Match(condition, Declare)->var, Var)->name;
|
||||
falsey_scope = fresh_scope(env);
|
||||
bind_statement(falsey_scope, condition);
|
||||
binding_t *b = get_binding(falsey_scope, varname);
|
||||
assert(b);
|
||||
|
||||
type_t *true_type = get_type(env, if_->body);
|
||||
type_t *false_type = get_type(env, if_->else_body);
|
||||
truthy_scope = fresh_scope(env);
|
||||
set_binding(truthy_scope, varname,
|
||||
new(binding_t, .type=Match(condition_type, OptionalType)->type, .code=b->code));
|
||||
|
||||
decl_code = compile_statement(env, condition);
|
||||
condition = Match(condition, Declare)->var;
|
||||
} else if (condition->tag == Var) {
|
||||
condition_type = get_type(env, condition);
|
||||
if (condition_type->tag == OptionalType) {
|
||||
truthy_scope = fresh_scope(env);
|
||||
const char *varname = Match(if_->condition, Var)->name;
|
||||
binding_t *b = get_binding(env, varname);
|
||||
if (!b) code_err(condition, "I don't know what this variable refers to");
|
||||
set_binding(truthy_scope, varname,
|
||||
new(binding_t, .type=Match(condition_type, OptionalType)->type, .code=b->code));
|
||||
}
|
||||
} else {
|
||||
condition_type = get_type(env, condition);
|
||||
}
|
||||
|
||||
if (condition_type->tag == PointerType)
|
||||
code_err(condition, "This pointer will always be non-null, so it should not be used in a conditional.");
|
||||
|
||||
CORD condition_code;
|
||||
if (condition_type->tag == TextType) {
|
||||
condition_code = CORD_all("(", compile(env, condition), ").length");
|
||||
} else if (condition_type->tag == ArrayType) {
|
||||
condition_code = CORD_all("(", compile(env, condition), ").length");
|
||||
} else if (condition_type->tag == TableType || condition_type->tag == SetType) {
|
||||
condition_code = CORD_all("(", compile(env, condition), ").entries.length");
|
||||
} else if (condition_type->tag == OptionalType) {
|
||||
condition_code = CORD_all("!", check_null(condition_type, compile(env, condition)));
|
||||
} else if (condition_type->tag == BoolType) {
|
||||
condition_code = compile(env, condition);
|
||||
} else {
|
||||
code_err(condition, "%T values cannot be used for conditionals", condition_type);
|
||||
}
|
||||
|
||||
type_t *true_type = get_type(truthy_scope, if_->body);
|
||||
type_t *false_type = get_type(falsey_scope, if_->else_body);
|
||||
if (true_type->tag == AbortType || true_type->tag == ReturnType)
|
||||
return CORD_all("({ if (", condition, ") ", compile_statement(env, if_->body),
|
||||
"\n", compile(env, if_->else_body), "; })");
|
||||
return CORD_all("({ ", decl_code, "if (", condition_code, ") ", compile_statement(truthy_scope, if_->body),
|
||||
"\n", compile(falsey_scope, if_->else_body), "; })");
|
||||
else if (false_type->tag == AbortType || false_type->tag == ReturnType)
|
||||
return CORD_all("({ if (!(", condition, ")) ", compile_statement(env, if_->else_body),
|
||||
"\n", compile(env, if_->body), "; })");
|
||||
return CORD_all("({ ", decl_code, "if (!(", condition_code, ")) ", compile_statement(falsey_scope, if_->else_body),
|
||||
"\n", compile(truthy_scope, if_->body), "; })");
|
||||
else if (decl_code != CORD_EMPTY)
|
||||
return CORD_all("({ ", decl_code, "(", condition_code, ") ? ", compile(truthy_scope, if_->body), " : ",
|
||||
compile(falsey_scope, if_->else_body), ";})");
|
||||
else
|
||||
return CORD_all("((", condition, ") ? ",
|
||||
compile(env, if_->body), " : ", compile(env, if_->else_body), ")");
|
||||
return CORD_all("((", condition_code, ") ? ",
|
||||
compile(truthy_scope, if_->body), " : ", compile(falsey_scope, if_->else_body), ")");
|
||||
}
|
||||
case Reduction: {
|
||||
auto reduction = Match(ast, Reduction);
|
||||
|
@ -2,15 +2,15 @@
|
||||
use libraylib.so
|
||||
use <raylib.h>
|
||||
use <raymath.h>
|
||||
use file
|
||||
|
||||
use ./world.tm
|
||||
|
||||
func main(map="map.txt"):
|
||||
func main(map=(./map.txt)):
|
||||
extern InitWindow:func(w:Int32, h:Int32, title:CString)->Void
|
||||
InitWindow(1600, 900, "raylib [core] example - 2d camera")
|
||||
|
||||
map_contents := when read(map) is Success(m): m
|
||||
map_contents := if contents := map:read():
|
||||
contents
|
||||
else: exit(code=1, "Could not find the game map: $map")
|
||||
|
||||
World.CURRENT:load_map(map_contents)
|
||||
|
@ -284,3 +284,31 @@ func main():
|
||||
= {!Int}
|
||||
>> [5?, !Int, !Int, 6?]:sorted()
|
||||
= [!Int, !Int, 5?, 6?]
|
||||
|
||||
do:
|
||||
>> value := if var := 5?:
|
||||
var
|
||||
else:
|
||||
0
|
||||
= 5
|
||||
|
||||
do:
|
||||
>> value := if var := !Int:
|
||||
var
|
||||
else:
|
||||
0
|
||||
= 0
|
||||
|
||||
do:
|
||||
>> opt := 5?
|
||||
>> if opt:
|
||||
>> opt
|
||||
else:
|
||||
>> opt
|
||||
|
||||
do:
|
||||
>> opt := !Int
|
||||
>> if opt:
|
||||
>> opt
|
||||
else:
|
||||
>> opt
|
||||
|
40
typecheck.c
40
typecheck.c
@ -1161,18 +1161,38 @@ type_t *get_type(env_t *env, ast_t *ast)
|
||||
|
||||
case If: {
|
||||
auto if_ = Match(ast, If);
|
||||
type_t *true_t = get_type(env, if_->body);
|
||||
if (if_->else_body) {
|
||||
type_t *false_t = get_type(env, if_->else_body);
|
||||
type_t *t_either = type_or_type(true_t, false_t);
|
||||
if (!t_either)
|
||||
code_err(if_->else_body,
|
||||
"I was expecting this block to have a %T value (based on earlier clauses), but it actually has a %T value.",
|
||||
true_t, false_t);
|
||||
return t_either;
|
||||
} else {
|
||||
if (!if_->else_body)
|
||||
return Type(VoidType);
|
||||
|
||||
env_t *truthy_scope = env;
|
||||
env_t *falsey_scope = env;
|
||||
if (if_->condition->tag == Declare) {
|
||||
type_t *condition_type = get_type(env, Match(if_->condition, Declare)->value);
|
||||
falsey_scope = fresh_scope(env);
|
||||
bind_statement(falsey_scope, if_->condition);
|
||||
|
||||
truthy_scope = fresh_scope(env);
|
||||
const char *varname = Match(Match(if_->condition, Declare)->var, Var)->name;
|
||||
set_binding(truthy_scope, varname,
|
||||
new(binding_t, .type=Match(condition_type, OptionalType)->type));
|
||||
} else if (if_->condition->tag == Var) {
|
||||
type_t *condition_type = get_type(env, if_->condition);
|
||||
if (condition_type->tag == OptionalType) {
|
||||
truthy_scope = fresh_scope(env);
|
||||
const char *varname = Match(if_->condition, Var)->name;
|
||||
set_binding(truthy_scope, varname,
|
||||
new(binding_t, .type=Match(condition_type, OptionalType)->type));
|
||||
}
|
||||
}
|
||||
|
||||
type_t *true_t = get_type(truthy_scope, if_->body);
|
||||
type_t *false_t = get_type(falsey_scope, if_->else_body);
|
||||
type_t *t_either = type_or_type(true_t, false_t);
|
||||
if (!t_either)
|
||||
code_err(if_->else_body,
|
||||
"I was expecting this block to have a %T value (based on earlier clauses), but it actually has a %T value.",
|
||||
true_t, false_t);
|
||||
return t_either;
|
||||
}
|
||||
|
||||
case When: {
|
||||
|
Loading…
Reference in New Issue
Block a user