89 lines
4 KiB
Text
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"
|
|
}
|