ludic/selfhost/check/check_ops.ludic

89 lines
4 KiB
Text

# check_ops.ludic — L4: operators. Arithmetic keeps to one kind of number (a float never meets a
# fixed but by a literal), text joins text only, a bool is not a number, and a comparison is
# between two things of one class.
function ck_cond(e: Node, t: pointer) -> void {
if ck_unknown(t) or (t == "bool") { return }
ck_err("cond", e, `a condition wants a bool and this is {ck_a(t)}`)
}
function ck_bin(e: Node) -> pointer {
let op = e.s
let l = ck_expr(e.a)
let r = ck_expr(e.b)
return ck_binop(e, op, l, r, e.a, e.b)
}
function ck_binop(e: Node, op: pointer, l: pointer, r: pointer, a: Node, b: Node) -> pointer {
if (op == "and") or (op == "or") or (op == "&&") or (op == "||") {
ck_cond(a, l)
ck_cond(b, r)
return "bool"
}
if (op == "==") or (op == "!=") or (op == "<") or (op == ">") or (op == "<=") or (op == ">=") {
ck_compare(e, op, l, r, a, b)
return "bool"
}
if (op == "+") or (op == "-") or (op == "*") or (op == "/") or (op == "%") { return ck_arith(e, op, l, r, a, b) }
if (op == "&") or (op == "|") or (op == "^") or (op == "<<") or (op == ">>") {
if (l == "long") or (r == "long") { return "long" }
return "int"
}
return "?"
}
function ck_compare(e: Node, op: pointer, l: pointer, r: pointer, a: Node, b: Node) -> void {
if ck_unknown(l) or ck_unknown(r) or (l == "null") or (r == "null") { return }
let cl = ck_class(l)
let cr = ck_class(r)
# a pointer holding text is compared with a string all through the runtime and the compiler
if ((l == "pointer") and (cr == "string")) or ((r == "pointer") and (cl == "string")) { return }
if ((l == "pointer") and (cr == "key")) or ((r == "pointer") and (cl == "key")) { return }
if cl != cr {
ck_err("compare", e, `{op} between {ck_a(l)} and {ck_a(r)}`)
return
}
if ck_is_rec(l) and ck_is_rec(r) and (l != r) { ck_err("compare", e, `{op} between {ck_a(l)} and {ck_a(r)} can never be true`) }
if ck_is_lit(a) or ck_is_lit(b) { return }
if (l == "fixed") and ck_is_float(r) { ck_err("fixed", e, `{op} between a fixed and {ck_a(r)}`) }
if (r == "fixed") and ck_is_float(l) { ck_err("fixed", e, `{op} between {ck_a(l)} and a fixed`) }
}
# `string` and the untyped `pointer` are both text to an operator (the emitter's is_textish)
function ck_is_text(t: pointer) -> bool { return (t == "string") or (t == "pointer") }
function ck_arith(e: Node, op: pointer, l: pointer, r: pointer, a: Node, b: Node) -> pointer {
if ck_is_key(l) or ck_is_key(r) {
ck_err("key", e, `{op} on a Key: a key names a text, it is not one - make the text with tr(key)`)
return "?"
}
if ck_is_text(l) or ck_is_text(r) {
if ck_unknown(l) or ck_unknown(r) { return "string" }
if ck_is_text(l) and ck_is_text(r) and (op == "+") { return "string" }
ck_err("text", e, `{op} of {ck_a(l)} and {ck_a(r)}: text joins text only - write string(x) for a number`)
return "?"
}
if ck_unknown(l) or ck_unknown(r) { return "?" }
if (l == "bool") or (r == "bool") {
ck_err("bool", e, `{op} on a bool`)
return "?"
}
if ck_is_num(l) and ck_is_num(r) { return ck_num_result(e, op, l, r, a, b) }
ck_err("kind", e, `{op} of {ck_a(l)} and {ck_a(r)}`)
return "?"
}
# a decimal literal is whichever of float and fixed the other side is
function ck_num_result(e: Node, op: pointer, l: pointer, r: pointer, a: Node, b: Node) -> pointer {
if (l == "fixed") and ck_is_float(r) {
if ck_is_lit(b) { return "fixed" }
if ck_is_lit(a) { return r }
ck_err("fixed", e, `{op} of a fixed and {ck_a(r)}: convert one with float(x) or fixed(x)`)
return "?"
}
if (r == "fixed") and ck_is_float(l) {
if ck_is_lit(a) { return "fixed" }
if ck_is_lit(b) { return l }
ck_err("fixed", e, `{op} of {ck_a(l)} and a fixed: convert one with float(x) or fixed(x)`)
return "?"
}
if (l == "double") or (r == "double") { return "double" }
if ck_is_float(l) { return l }
if ck_is_float(r) { return r }
if (l == "fixed") or (r == "fixed") { return "fixed" }
if (l == "long") or (r == "long") { return "long" }
return "int"
}