mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-17 11:22:22 -07:00
267 lines
7.2 KiB
Odin
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;
|
|
}
|