mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-26 09:23:48 +00:00
sync with main
This commit is contained in:
@@ -0,0 +1,266 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user