Files
Odin/core/bindgen/c-parser-evaluate.odin
T
phillvancejr 42364f2fce sync with main
2022-02-04 13:16:40 -05:00

267 lines
7.2 KiB
Odin

package bindgen
import "core:fmt"
import "core:strconv"
// Evaluates an expression to a i64, without checking.
evaluate_i64 :: proc(data : ^ParserData) -> i64 {
ok : bool;
value : LiteralValue;
value, ok = evaluate(data);
return value.(i64);
}
// Evaluate an expression, returns whether it succeeded.
evaluate :: proc(data : ^ParserData) -> (LiteralValue, bool) {
return evaluate_level_5(data);
}
// @note Evaluate levels numbers are based on
// https://en.cppreference.com/w/c/language/operator_precedence.
// Bitwise shift level.
evaluate_level_5 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) {
value, ok = evaluate_level_4(data);
if !ok do return;
invalid_value : LiteralValue;
token := peek_token(data);
if token == "<<" {
v : LiteralValue;
eat_token(data);
v, ok = evaluate_level_5(data);
if is_i64(v) do value = value.(i64) << cast(u64) v.(i64);
else do invalid_value = v;
} else if token == ">>" {
v : LiteralValue;
eat_token(data);
v, ok = evaluate_level_5(data);
if is_i64(v) do value = value.(i64) >> cast(u64) v.(i64);
else do invalid_value = v;
}
if invalid_value != nil {
print_warning("Invalid operand for bitwise shift ", invalid_value);
}
return;
}
// Additive level.
evaluate_level_4 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) {
value, ok = evaluate_level_3(data);
if !ok do return;
token := peek_token(data);
if token == "+" {
v : LiteralValue;
eat_token(data);
v, ok = evaluate_level_4(data);
if is_i64(v) do value = value.(i64) + v.(i64);
else if is_f64(v) do value = value.(f64) + v.(f64);
}
else if token == "-" {
v : LiteralValue;
eat_token(data);
v, ok = evaluate_level_4(data);
if is_i64(v) do value = value.(i64) - v.(i64);
else if is_f64(v) do value = value.(f64) - v.(f64);
}
return;
}
// Multiplicative level.
evaluate_level_3 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) {
value, ok = evaluate_level_2(data);
if !ok do return;
token := peek_token(data);
if token == "*" {
v : LiteralValue;
eat_token(data);
v, ok = evaluate_level_3(data);
if is_i64(v) do value = value.(i64) * v.(i64);
else if is_f64(v) do value = value.(f64) * v.(f64);
}
else if token == "/" {
v : LiteralValue;
eat_token(data);
v, ok = evaluate_level_3(data);
if is_i64(v) do value = value.(i64) / v.(i64);
else if is_f64(v) do value = value.(f64) / v.(f64);
}
return;
}
// Prefix level.
evaluate_level_2 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) {
token := peek_token(data);
// Bitwise not
if token == "~" {
check_and_eat_token(data, "~");
value, ok = evaluate_level_2(data);
value = ~value.(i64);
}
else {
// @note Should call evaluate_level_1, but we don't have that because we do not dereferenciation.
value, ok = evaluate_level_0(data);
}
return;
}
// Does not try to compose with arithmetics, it just evaluates one single expression.
evaluate_level_0 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) {
ok = true;
value = 0;
token := peek_token(data);
// Parentheses
if token == "(" {
value, ok = evaluate_parentheses(data);
} // Number literal
else if (token[0] == '-') || (token[0] >= '0' && token[0] <= '9') {
value, ok = evaluate_number_literal(data);
} // String literal
else if token[0] == '"' {
value = evaluate_string_literal(data);
} // Function-like
else if token == "sizeof" {
value = evaluate_sizeof(data);
} // Knowned literal
else if token in data.knownedLiterals {
value = evaluate_knowned_literal(data);
} // Custom expression
else if token in data.options.customExpressionHandlers {
value = data.options.customExpressionHandlers[token](data);
}
else {
print_warning("Unknown token ", token, " for expression evaluation.");
ok = false;
}
return;
}
evaluate_sizeof :: proc(data : ^ParserData) -> LiteralValue {
print_warning("Using 'sizeof()'. Currently not able to precompute that. Please check generated code.");
check_and_eat_token(data, "sizeof");
check_and_eat_token(data, "(");
for data.bytes[data.offset] != ')' {
data.offset += 1;
}
check_and_eat_token(data, ")");
return 1;
}
evaluate_parentheses :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) {
check_and_eat_token(data, "(");
// Cast to int (via "(int)" syntax)
token := peek_token(data);
if token == "int" {
check_and_eat_token(data, "int");
check_and_eat_token(data, ")");
value, ok = evaluate(data);
return;
} // Cast to enum value (via "(enum XXX)" syntax)
else if token == "enum" {
check_and_eat_token(data, "enum");
eat_token(data);
check_and_eat_token(data, ")");
value, ok = evaluate(data);
return;
}
value, ok = evaluate(data);
check_and_eat_token(data, ")");
return;
}
evaluate_number_literal :: proc(data : ^ParserData, loc := #caller_location) -> (value : LiteralValue, ok : bool) {
token := parse_any(data);
// Unary - before numbers
numberLitteral := token;
for token == "-" {
token = parse_any(data);
numberLitteral = tcat(numberLitteral, token);
}
token = numberLitteral;
// Check if any point or scientific notation in number
foundPointOrExp := false;
for c in token {
if c == '.' || c == 'e' || c == 'E' {
foundPointOrExp = true;
break;
}
}
isHexadecimal := len(token) >= 2 && token[:2] == "0x";
// Computing postfix
tokenLength := len(token);
l := tokenLength - 1;
for l > 0 {
c := token[l];
if c >= '0' && c <= '9' { break; }
if isHexadecimal && ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { break; }
l -= 1;
}
postfix : string;
if l != tokenLength - 1 {
postfix = token[l+1:];
token = token[:l+1];
}
if postfix != "" && (postfix[0] == 'u' || postfix[0] == 'U') {
print_warning("Found number litteral '", token, "' with unsigned postfix, we cast it to an int64 internally.");
}
// Floating point
if !isHexadecimal && (foundPointOrExp || postfix == "f") {
value, ok = strconv.parse_f64(token);
} // Integer
else {
value, ok = strconv.parse_i64(token);
}
if !ok {
print_error(data, loc, "Expected number litteral but got '", token, "'.");
}
return value, ok;
}
evaluate_string_literal :: proc(data : ^ParserData) -> string {
token := parse_any(data);
return token;
}
evaluate_knowned_literal :: proc(data : ^ParserData) -> LiteralValue {
token := parse_any(data);
return data.knownedLiterals[token];
}
is_i64 :: proc(value : LiteralValue) -> (ok : bool) {
v : i64;
v, ok = value.(i64);
return ok;
}
is_f64 :: proc(value : LiteralValue) -> (ok : bool) {
v : f64;
v, ok = value.(f64);
return ok;
}