diff options
| author | phillvancejr <phillipvancejr@gmail.com> | 2022-02-04 13:45:36 -0500 |
|---|---|---|
| committer | phillvancejr <phillipvancejr@gmail.com> | 2022-02-04 13:45:36 -0500 |
| commit | a315e7c9628cc0b53c4504fa6432a75d5ecc2fd8 (patch) | |
| tree | 03b7e181bd6fed85c8ed6b762660c8a02607b9ca | |
| parent | 42364f2fcee1f0f588b006bf2b7e9bc6f88acb93 (diff) | |
Reverted experimental changes that were meant for a branch, in sync with master
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | core/bindgen/c-parser-evaluate.odin | 266 | ||||
| -rw-r--r-- | core/bindgen/c-parser-helpers.odin | 267 | ||||
| -rw-r--r-- | core/bindgen/c-parser-nodes.odin | 132 | ||||
| -rw-r--r-- | core/bindgen/c-parser.odin | 840 | ||||
| -rw-r--r-- | core/bindgen/errors.odin | 44 | ||||
| -rw-r--r-- | core/bindgen/generator-clean.odin | 284 | ||||
| -rw-r--r-- | core/bindgen/generator-export.odin | 166 | ||||
| -rw-r--r-- | core/bindgen/generator-helpers.odin | 392 | ||||
| -rw-r--r-- | core/bindgen/generator.odin | 205 | ||||
| l--------- | wasm-ld | 1 |
11 files changed, 1 insertions, 2598 deletions
@@ -17,7 +17,7 @@ ifeq ($(OS), Darwin) LLVM_VERSIONS = "13.%.%" else # allow for x86 / amd64 all llvm versions begining from 11 - LLVM_VERSIONS = "13.%.%" "12.0.1" "11.0.0" + LLVM_VERSIONS = "13.%.%" "12.0.1" "11.1.0" endif LLVM_VERSION_PATTERN_SEPERATOR = )|( diff --git a/core/bindgen/c-parser-evaluate.odin b/core/bindgen/c-parser-evaluate.odin deleted file mode 100644 index 13cb5042c..000000000 --- a/core/bindgen/c-parser-evaluate.odin +++ /dev/null @@ -1,266 +0,0 @@ -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; -} diff --git a/core/bindgen/c-parser-helpers.odin b/core/bindgen/c-parser-helpers.odin deleted file mode 100644 index a99d83dd2..000000000 --- a/core/bindgen/c-parser-helpers.odin +++ /dev/null @@ -1,267 +0,0 @@ -package bindgen - -import "core:os" -import "core:fmt" -import "core:strings" -import "core:strconv" - -// Extract from start (included) to end (excluded) offsets -extract_string :: proc(data : ^ParserData, startOffset : u32, endOffset : u32) -> string { - return strings.string_from_ptr(&data.bytes[startOffset], cast(int) (endOffset - startOffset)); -} - -// Peek the end offset of the next token -peek_token_end :: proc(data : ^ParserData) -> u32 { - offset : u32; - - for true { - eat_whitespaces_and_comments(data); - if data.offset >= data.bytesLength { - return data.bytesLength; - } - offset = data.offset; - - // Identifier - if (data.bytes[offset] >= 'a' && data.bytes[offset] <= 'z') || - (data.bytes[offset] >= 'A' && data.bytes[offset] <= 'Z') || - (data.bytes[offset] == '_') { - offset += 1; - for (data.bytes[offset] >= 'a' && data.bytes[offset] <= 'z') || - (data.bytes[offset] >= 'A' && data.bytes[offset] <= 'Z') || - (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') || - (data.bytes[offset] == '_') { - offset += 1; - } - } - if offset != data.offset { - // Nothing to do: we found an identifier - } // Number literal - else if (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') { - offset += 1; - // Hexademical literal - if data.bytes[offset - 1] == '0' && data.bytes[offset] == 'x' { - offset += 1; - for (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') || - (data.bytes[offset] >= 'a' && data.bytes[offset] <= 'f') || - (data.bytes[offset] >= 'A' && data.bytes[offset] <= 'F') { - offset += 1; - } - } // Basic number literal - else { - for (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') || - data.bytes[offset] == '.' { - offset += 1; - } - - if (data.bytes[offset] == 'e' || data.bytes[offset] == 'E') { - offset += 1; - if data.bytes[offset] == '-' { - offset += 1; - } - } - - for (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') { - offset += 1; - } - } - - // Number suffix? - for (data.bytes[offset] == 'u' || data.bytes[offset] == 'U') || - (data.bytes[offset] == 'l' || data.bytes[offset] == 'L') || - (data.bytes[offset] == 'f') { - offset += 1; - } - } // String literal - else if data.bytes[offset] == '"' { - offset += 1; - for data.bytes[offset-1] == '\\' || data.bytes[offset] != '"' { - offset += 1; - } - offset += 1; - } // Possible shifts - else if data.bytes[offset] == '<' || data.bytes[offset] == '>' { - offset += 1; - if data.bytes[offset] == data.bytes[offset-1] { - offset += 1; - } - } // Single character - else { - offset += 1; - } - - token := extract_string(data, data.offset, offset); - - // Ignore __attribute__ - if token == "__attribute__" { - print_warning("__attribute__ is ignored."); - - for data.bytes[offset] != '(' { - offset += 1; - } - - parenthesesCount := 1; - for true { - offset += 1; - if data.bytes[offset] == '(' do parenthesesCount += 1; - else if data.bytes[offset] == ')' do parenthesesCount -= 1; - if parenthesesCount == 0 do break; - } - offset += 1; - - data.offset = offset; - } // Ignore certain keywords - else if (token == "inline" || token == "__inline" || token == "static" - || token == "restrict" || token == "__restrict" - || token == "volatile" - || token == "__extension__") { - data.offset = offset; - } // Ignore ignored tokens ;) - else { - for ignoredToken in data.options.ignoredTokens { - if token == ignoredToken { - data.offset = offset; - break; - } - } - } - - if data.offset != offset { - break; - } - } - - return offset; -} - -// Peek the next token (just eating whitespaces and comment) -peek_token :: proc(data : ^ParserData) -> string { - tokenEnd := peek_token_end(data); - if tokenEnd == data.bytesLength { - return "EOF"; - } - return extract_string(data, data.offset, tokenEnd); -} - -// Find the end of the define directive (understanding endline backslashes) -// @note Tricky cases like comments hiding a backslash effect are not handled. -peek_define_end :: proc(data : ^ParserData) -> u32 { - defineEndOffset := data.offset; - for data.bytes[defineEndOffset-1] == '\\' || data.bytes[defineEndOffset] != '\n' { - defineEndOffset += 1; - } - return defineEndOffset; -} - -eat_comment :: proc(data : ^ParserData) { - if data.offset >= data.bytesLength || data.bytes[data.offset] != '/' { - return; - } - - // Line comment - if data.bytes[data.offset + 1] == '/' { - eat_line(data); - } // Range comment - else if data.bytes[data.offset + 1] == '*' { - data.offset += 2; - for data.bytes[data.offset] != '*' || data.bytes[data.offset + 1] != '/' { - data.offset += 1; - } - data.offset += 2; - } -} - -// Eat whitespaces -eat_whitespaces :: proc(data : ^ParserData) { - // Effective whitespace - for data.offset < data.bytesLength && - (data.bytes[data.offset] == ' ' || data.bytes[data.offset] == '\t' || - data.bytes[data.offset] == '\r' || data.bytes[data.offset] == '\n') { - if data.bytes[data.offset] == '\n' && data.bytes[data.offset] != '\\' { - data.foundFullReturn = true; - } - data.offset += 1; - } -} - -// Removes whitespaces and comments -eat_whitespaces_and_comments :: proc(data : ^ParserData) { - startOffset : u32 = 0xFFFFFFFF; - for startOffset != data.offset { - startOffset = data.offset; - eat_whitespaces(data); - eat_comment(data); - } -} - -// Eat full line -eat_line :: proc(data : ^ParserData) { - for ; data.bytes[data.offset] != '\n'; data.offset += 1 { - } -} - -// Eat a line, and repeat if it ends with a backslash -eat_define_lines :: proc(data : ^ParserData) { - for data.bytes[data.offset-1] == '\\' || data.bytes[data.offset] != '\n' { - data.offset += 1; - } -} - -// Eat next token -eat_token :: proc(data : ^ParserData) { - data.offset = peek_token_end(data); -} - -// Eat next token -check_and_eat_token :: proc(data : ^ParserData, expectedToken : string, loc := #caller_location) { - token := peek_token(data); - if token != expectedToken { - print_error(data, loc, "Expected ", expectedToken, " but found ", token, "."); - } - data.offset += cast(u32) len(token); -} - -// Check whether the next token is outside #define range -is_define_end :: proc(data : ^ParserData) -> bool { - defineEnd := peek_define_end(data); - tokenEnd := peek_token_end(data); - - return (defineEnd < tokenEnd); -} - -// Check if the current #define is a macro definition -is_define_macro :: proc(data : ^ParserData) -> bool { - startOffset := data.offset; - defer data.offset = startOffset; - - token := parse_any(data); - if token != "(" do return false; - - // Find the other parenthesis - parenthesesCount := 1; - for parenthesesCount != 0 { - token = parse_any(data); - if token == "(" do parenthesesCount += 1; - else if token == ")" do parenthesesCount -= 1; - } - - // Its a macro if after the parentheses, it's not the end - return !is_define_end(data); -} - -// @note Very slow function to get line number, -// use only for errors. -// @todo Well, this does not seem to work properly, UTF-8 problem? -get_line_column :: proc(data : ^ParserData) -> (u32, u32) { - line : u32 = 1; - column : u32 = 0; - for i : u32 = 0; i < data.offset; i += 1 { - if data.bytes[i] == '\n' { - column = 0; - line += 1; - } - else { - column += 1; - } - } - return line, column; -} diff --git a/core/bindgen/c-parser-nodes.odin b/core/bindgen/c-parser-nodes.odin deleted file mode 100644 index 0620e0187..000000000 --- a/core/bindgen/c-parser-nodes.odin +++ /dev/null @@ -1,132 +0,0 @@ -package bindgen - -DefineNode :: struct { - name : string, - value : LiteralValue, -} - -StructDefinitionNode :: struct { - name : string, - members : [dynamic]StructOrUnionMember, - forwardDeclared : bool, -} - -UnionDefinitionNode :: struct { - name : string, - members : [dynamic]StructOrUnionMember, -} - -EnumDefinitionNode :: struct { - name : string, - members : [dynamic]EnumMember, -} - -FunctionDeclarationNode :: struct { - name : string, - returnType : Type, - parameters : [dynamic]FunctionParameter, -} - -TypedefNode :: struct { - name : string, - type : Type, -} - -Nodes :: struct { - defines : [dynamic]DefineNode, - enumDefinitions : [dynamic]EnumDefinitionNode, - unionDefinitions : [dynamic]UnionDefinitionNode, - structDefinitions : [dynamic]StructDefinitionNode, - functionDeclarations : [dynamic]FunctionDeclarationNode, - typedefs : [dynamic]TypedefNode, -} - -LiteralValue :: union { - i64, - f64, - string, -} - -// Type, might be an array -Type :: struct { - base : BaseType, - dimensions : [dynamic]u64, // Array dimensions -} - -BaseType :: union { - BuiltinType, - PointerType, - IdentifierType, - FunctionType, - FunctionPointerType, -} - -BuiltinType :: enum { - Unknown, - Void, - Int, - UInt, - LongInt, - ULongInt, - LongLongInt, - ULongLongInt, - ShortInt, - UShortInt, - Char, - SChar, - UChar, - Float, - Double, - LongDouble, - - // Not defined by C language but in <stdint.h> - Int8, - Int16, - Int32, - Int64, - UInt8, - UInt16, - UInt32, - UInt64, - Size, - SSize, - PtrDiff, - UIntPtr, - IntPtr, -} - -PointerType :: struct { - type : ^Type, // Pointer is there to prevent definition cycle. Null means void. -} - -IdentifierType :: struct { - name : string, - anonymous : bool, // An anonymous identifier can be hard-given a name in some contexts. -} - -FunctionType :: struct { - returnType : ^Type, // Pointer is there to prevent definition cycle. Null means void. - parameters : [dynamic]FunctionParameter, -} - -FunctionPointerType :: struct { - name : string, - returnType : ^Type, // Pointer is there to prevent definition cycle. Null means void. - parameters : [dynamic]FunctionParameter, -} - -EnumMember :: struct { - name : string, - value : i64, - hasValue : bool, -} - -StructOrUnionMember :: struct { - name : string, - type : Type, -} - -FunctionParameter :: struct { - name : string, - type : Type, -} diff --git a/core/bindgen/c-parser.odin b/core/bindgen/c-parser.odin deleted file mode 100644 index c3ef4937f..000000000 --- a/core/bindgen/c-parser.odin +++ /dev/null @@ -1,840 +0,0 @@ -package bindgen - -import "core:os" -import "core:fmt" -import "core:strings" -import "core:strconv" - -// Global counters -anonymousStructCount := 0; -anonymousUnionCount := 0; -anonymousEnumCount := 0; - -knownTypeAliases : map[string]Type; - -CustomHandler :: proc(data : ^ParserData); -CustomExpressionHandler :: proc(data : ^ParserData) -> LiteralValue; - -ParserOptions :: struct { - ignoredTokens : []string, - - // Handlers - customHandlers : map[string]CustomHandler, - customExpressionHandlers : map[string]CustomExpressionHandler, -} - -ParserData :: struct { - bytes : []u8, - bytesLength : u32, - offset : u32, - - // References - nodes : Nodes, - options : ^ParserOptions, - - // Knowned values - knownedLiterals : map[string]LiteralValue, - - // Whether we have eaten a '\n' character that has no backslash just before - foundFullReturn : bool, -} - -is_identifier :: proc(token : string) -> bool { - return (token[0] >= 'a' && token[0] <= 'z') || - (token[0] >= 'A' && token[0] <= 'Z') || - (token[0] == '_'); -} - -parse :: proc(bytes : []u8, options : ParserOptions, loc := #caller_location) -> Nodes { - options := options; - - data : ParserData; - data.bytes = bytes; - data.bytesLength = cast(u32) len(bytes); - data.options = &options; - - for data.offset = 0; data.offset < data.bytesLength; { - token := peek_token(&data); - if data.offset == data.bytesLength do break; - - if token in options.customHandlers { - options.customHandlers[token](&data); - } - else if token == "{" || token == "}" || token == ";" { - eat_token(&data); - } - else if token == "extern" { - check_and_eat_token(&data, "extern"); - } - else if token == "\"C\"" { - check_and_eat_token(&data, "\"C\""); - } - else if token == "#" { - parse_directive(&data); - } - else if token == "typedef" { - parse_typedef(&data); - } - else if is_identifier(token) { - parse_variable_or_function_declaration(&data); - } - else { - print_error(&data, loc, "Unexpected token: ", token, "."); - return data.nodes; - } - } - - return data.nodes; -} - -parse_any :: proc(data : ^ParserData) -> string { - offset := peek_token_end(data); - identifier := extract_string(data, data.offset, offset); - data.offset = offset; - return identifier; -} - -parse_identifier :: proc(data : ^ParserData, loc := #caller_location) -> string { - identifier := parse_any(data); - - if (identifier[0] < 'a' || identifier[0] > 'z') && - (identifier[0] < 'A' || identifier[0] > 'Z') && - (identifier[0] != '_') { - print_error(data, loc, "Expected identifier but found ", identifier, "."); - } - - return identifier; -} - -parse_type_dimensions :: proc(data : ^ParserData, type : ^Type) { - token := peek_token(data); - for token == "[" { - eat_token(data); - token = peek_token(data); - if token == "]" { - pointerType : PointerType; - pointerType.type = new(Type); - pointerType.type^ = type^; // Copy - type.base = pointerType; - delete(type.dimensions); - } else { - dimension := evaluate_i64(data); - append(&type.dimensions, cast(u64) dimension); - } - check_and_eat_token(data, "]"); - token = peek_token(data); - } -} - -// This will parse anything that look like a type: -// Builtin: char/int/float/... -// Struct-like: struct A/struct { ... }/enum E -// Function pointer: void (*f)(...) -// -// Definition permitted: If a struct-like definition is found, it will generate -// the according Node and return a corresponding type. -parse_type :: proc(data : ^ParserData, definitionPermitted := false) -> Type { - type : Type; - - // Eat qualifiers - token := peek_token(data); - if token == "const" { - eat_token(data); - token = peek_token(data); - } - - // Parse main type - if token == "struct" { - type.base = parse_struct_type(data, definitionPermitted); - } - else if token == "union" { - type.base = parse_union_type(data); - } - else if token == "enum" { - type.base = parse_enum_type(data); - } - else { - // Test builtin type - type.base = parse_builtin_type(data); - if type.base.(BuiltinType) == BuiltinType.Unknown { - // Basic identifier type - identifierType : IdentifierType; - identifierType.name = parse_identifier(data); - type.base = identifierType; - } - } - - // Eat qualifiers - token = peek_token(data); - if token == "const" { - eat_token(data); - token = peek_token(data); - } - - // Check if pointer - for token == "*" { - check_and_eat_token(data, "*"); - token = peek_token(data); - - pointerType : PointerType; - pointerType.type = new(Type); - pointerType.type^ = type; // Copy - - type.base = pointerType; - - // Eat qualifiers - if token == "const" { - eat_token(data); - token = peek_token(data); - } - } - - // Parse array dimensions if any. - parse_type_dimensions(data, &type); - - // ----- Function pointer type - - if token == "(" { - check_and_eat_token(data, "("); - check_and_eat_token(data, "*"); - - functionPointerType : FunctionPointerType; - functionPointerType.returnType = new(Type); - functionPointerType.returnType^ = type; - functionPointerType.name = parse_identifier(data); - - check_and_eat_token(data, ")"); - parse_function_parameters(data, &functionPointerType.parameters); - - type.base = functionPointerType; - } - - return type; -} - -parse_builtin_type :: proc(data : ^ParserData) -> BuiltinType { - previousBuiltinType := BuiltinType.Unknown; - intFound := false; - shortFound := false; - signedFound := false; - unsignedFound := false; - longCount := 0; - - for true { - token := peek_token(data); - - // Attribute - attributeFound := true; - if token == "long" do longCount += 1; - else if token == "short" do shortFound = true; - else if token == "unsigned" do unsignedFound = true; - else if token == "signed" do signedFound = true; - else do attributeFound = false; - if attributeFound { eat_token(data); continue; } - - // Known type alias - if token in knownTypeAliases { - builtinType, ok := knownTypeAliases[token].base.(BuiltinType); - if ok { - eat_token(data); - previousBuiltinType = builtinType; - } - break; - } - - // Classic type and standard types - if token == "void" { eat_token(data); return BuiltinType.Void; } - else if token == "int" { - eat_token(data); - intFound = true; - } - else if token == "float" { eat_token(data); return BuiltinType.Float; } - else if token == "double" { - eat_token(data); - if longCount == 0 do return BuiltinType.Double; - else do return BuiltinType.LongDouble; - } - else if token == "char" { - eat_token(data); - if signedFound do return BuiltinType.SChar; - else if unsignedFound do return BuiltinType.UChar; - else do return BuiltinType.Char; - } - else if token == "__int8" { - // @note :MicrosoftDumminess __intX are Microsoft's fixed-size integers - // https://docs.microsoft.com/fr-fr/cpp/cpp/int8-int16-int32-int64 - // and for unsigned version, they prefixed it with "unsigned"... - eat_token(data); - if unsignedFound do return BuiltinType.UInt8; - else do return BuiltinType.Int8; - } - else if token == "__int16" { - eat_token(data); - if unsignedFound do return BuiltinType.UInt16; - else do return BuiltinType.Int16; - } - else if token == "__int32" { - eat_token(data); - if unsignedFound do return BuiltinType.UInt32; - else do return BuiltinType.Int32; - } - else if token == "__int64" { - eat_token(data); - if unsignedFound do return BuiltinType.UInt64; - else do return BuiltinType.Int64; - } - else if token == "int8_t" { eat_token(data); return BuiltinType.Int8; } - else if token == "int16_t" { eat_token(data); return BuiltinType.Int16; } - else if token == "int32_t" { eat_token(data); return BuiltinType.Int32; } - else if token == "int64_t" { eat_token(data); return BuiltinType.Int64; } - else if token == "uint8_t" { eat_token(data); return BuiltinType.UInt8; } - else if token == "uint16_t" { eat_token(data); return BuiltinType.UInt16; } - else if token == "uint32_t" { eat_token(data); return BuiltinType.UInt32; } - else if token == "uint64_t" { eat_token(data); return BuiltinType.UInt64; } - else if token == "size_t" { eat_token(data); return BuiltinType.Size; } - else if token == "ssize_t" { eat_token(data); return BuiltinType.SSize; } - else if token == "ptrdiff_t" { eat_token(data); return BuiltinType.PtrDiff; } - else if token == "uintptr_t" { eat_token(data); return BuiltinType.UIntPtr; } - else if token == "intptr_t" { eat_token(data); return BuiltinType.IntPtr; } - - break; - } - - // Adapt previous builtin type - if previousBuiltinType == BuiltinType.ShortInt { - shortFound = true; - } - else if previousBuiltinType == BuiltinType.Int { - intFound = true; - } - else if previousBuiltinType == BuiltinType.LongInt { - longCount += 1; - } - else if previousBuiltinType == BuiltinType.LongLongInt { - longCount += 2; - } - else if previousBuiltinType == BuiltinType.UShortInt { - unsignedFound = true; - shortFound = true; - } - else if previousBuiltinType == BuiltinType.UInt { - unsignedFound = true; - } - else if previousBuiltinType == BuiltinType.ULongInt { - unsignedFound = true; - longCount += 1; - } - else if previousBuiltinType == BuiltinType.ULongLongInt { - unsignedFound = true; - longCount += 2; - } - else if (previousBuiltinType != BuiltinType.Unknown) { - return previousBuiltinType; // float, void, etc. - } - - // Implicit and explicit int - if intFound || shortFound || unsignedFound || signedFound || longCount > 0 { - if unsignedFound { - if shortFound do return BuiltinType.UShortInt; - if longCount == 0 do return BuiltinType.UInt; - if longCount == 1 do return BuiltinType.ULongInt; - if longCount == 2 do return BuiltinType.ULongLongInt; - } else { - if shortFound do return BuiltinType.ShortInt; - if longCount == 0 do return BuiltinType.Int; - if longCount == 1 do return BuiltinType.LongInt; - if longCount == 2 do return BuiltinType.LongLongInt; - } - } - - return BuiltinType.Unknown; -} - -parse_struct_type :: proc(data : ^ParserData, definitionPermitted : bool) -> IdentifierType { - check_and_eat_token(data, "struct"); - - type : IdentifierType; - token := peek_token(data); - - if !definitionPermitted || token != "{" { - type.name = parse_identifier(data); - token = peek_token(data); - } else { - type.name = tcat("AnonymousStruct", anonymousStructCount); - type.anonymous = true; - anonymousStructCount += 1; - } - - if token == "{" { - node := parse_struct_definition(data); - node.name = type.name; - } else if definitionPermitted { - // @note Whatever happens, we create a definition of the struct, - // as it might be used to forward declare it and then use it only with a pointer. - // This for instance the pattern for xcb_connection_t which definition - // is never known from user API. - node : StructDefinitionNode; - node.forwardDeclared = false; - node.name = type.name; - append(&data.nodes.structDefinitions, node); - } - - return type; -} - -parse_union_type :: proc(data : ^ParserData) -> IdentifierType { - check_and_eat_token(data, "union"); - - type : IdentifierType; - token := peek_token(data); - - if token != "{" { - type.name = parse_identifier(data); - token = peek_token(data); - } else { - type.name = tcat("AnonymousUnion", anonymousUnionCount); - type.anonymous = true; - anonymousUnionCount += 1; - } - - if token == "{" { - node := parse_union_definition(data); - node.name = type.name; - } - - return type; -} - -parse_enum_type :: proc(data : ^ParserData) -> IdentifierType { - check_and_eat_token(data, "enum"); - - type : IdentifierType; - token := peek_token(data); - - if token != "{" { - type.name = parse_identifier(data); - token = peek_token(data); - } else { - type.name = tcat("AnonymousEnum", anonymousEnumCount); - type.anonymous = true; - anonymousEnumCount += 1; - } - - if token == "{" { - node := parse_enum_definition(data); - node.name = type.name; - } - - return type; -} - -/** - * We only care about defines of some value - */ -parse_directive :: proc(data : ^ParserData) { - check_and_eat_token(data, "#"); - - token := peek_token(data); - if token == "define" { - parse_define(data); - } // We ignore all other directives - else { - eat_line(data); - } -} - -parse_define :: proc(data : ^ParserData) { - check_and_eat_token(data, "define"); - data.foundFullReturn = false; - - node : DefineNode; - node.name = parse_identifier(data); - - // Does it look like end? It might be a #define with no expression - if is_define_end(data) { - node.value = 1; - append(&data.nodes.defines, node); - data.knownedLiterals[node.name] = node.value; - } // Macros are ignored - else if is_define_macro(data) { - print_warning("Ignoring define macro for ", node.name, "."); - } - else { - literalValue, ok := evaluate(data); - if ok { - node.value = literalValue; - append(&data.nodes.defines, node); - data.knownedLiterals[node.name] = node.value; - } - else { - print_warning("Ignoring define expression for ", node.name, "."); - } - } - - // Evaluating the expression, we might have already eaten a full return, - // if so, do nothing. - if !data.foundFullReturn { - eat_define_lines(data); - } -} - -// @fixme Move -change_anonymous_node_name :: proc (data : ^ParserData, oldName : string, newName : string) -> bool { - for i := 0; i < len(data.nodes.structDefinitions); i += 1 { - if data.nodes.structDefinitions[i].name == oldName { - data.nodes.structDefinitions[i].name = newName; - return true; - } - } - - for i := 0; i < len(data.nodes.enumDefinitions); i += 1 { - if data.nodes.enumDefinitions[i].name == oldName { - data.nodes.enumDefinitions[i].name = newName; - return true; - } - } - - for i := 0; i < len(data.nodes.unionDefinitions); i += 1 { - if data.nodes.unionDefinitions[i].name == oldName { - data.nodes.unionDefinitions[i].name = newName; - return true; - } - } - - return false; -} - -/** - * Type aliasing. - * typedef <sourceType> <name>; - */ -parse_typedef :: proc(data : ^ParserData) { - check_and_eat_token(data, "typedef"); - - // @note Struct-like definitions (and such) - // are generated within type parsing. - // - // So that typedef struct { int foo; }* Ap; is valid. - - // Parsing type - node : TypedefNode; - node.type = parse_type(data, true); - - if sourceType, ok := node.type.base.(FunctionPointerType); ok { - node.name = sourceType.name; - } else { - node.name = parse_identifier(data); - } - - // Checking if function type - token := peek_token(data); - if token == "(" { - functionType : FunctionType; - functionType.returnType = new(Type); - functionType.returnType^ = node.type; - - parse_function_parameters(data, &functionType.parameters); - - node.type.base = functionType; - } - - // Checking if array - parse_type_dimensions(data, &node.type); - - // If the underlying type is anonymous, - // we just affect it the name. - addTypedefNode := true; - if identifierType, ok := node.type.base.(IdentifierType); ok { - if identifierType.anonymous { - addTypedefNode = !change_anonymous_node_name(data, identifierType.name, node.name); - } - } - - if addTypedefNode { - knownTypeAliases[node.name] = node.type; - append(&data.nodes.typedefs, node); - } - - check_and_eat_token(data, ";"); - - // @note Commented tool for debug - // fmt.println("Typedef: ", node.type, node.name); -} - -parse_struct_definition :: proc(data : ^ParserData) -> ^StructDefinitionNode { - node : StructDefinitionNode; - node.forwardDeclared = false; - parse_struct_or_union_members(data, &node.members); - - append(&data.nodes.structDefinitions, node); - return &data.nodes.structDefinitions[len(data.nodes.structDefinitions) - 1]; -} - -parse_union_definition :: proc(data : ^ParserData) -> ^UnionDefinitionNode { - node : UnionDefinitionNode; - parse_struct_or_union_members(data, &node.members); - - append(&data.nodes.unionDefinitions, node); - return &data.nodes.unionDefinitions[len(data.nodes.unionDefinitions) - 1]; -} - -parse_enum_definition :: proc(data : ^ParserData) -> ^EnumDefinitionNode { - node : EnumDefinitionNode; - parse_enum_members(data, &node.members); - - append(&data.nodes.enumDefinitions, node); - return &data.nodes.enumDefinitions[len(data.nodes.enumDefinitions) - 1]; -} - -/** - * { - * <name> = <value>, - * <name>, - * } - */ -parse_enum_members :: proc(data : ^ParserData, members : ^[dynamic]EnumMember) { - check_and_eat_token(data, "{"); - - nextMemberValue : i64 = 0; - token := peek_token(data); - for token != "}" { - member : EnumMember; - member.name = parse_identifier(data); - member.hasValue = false; - - token = peek_token(data); - if token == "=" { - check_and_eat_token(data, "="); - - member.hasValue = true; - member.value = evaluate_i64(data); - nextMemberValue = member.value; - token = peek_token(data); - } else { - member.value = nextMemberValue; - } - - data.knownedLiterals[member.name] = member.value; - nextMemberValue += 1; - - // Eat until end, as this might be a complex expression that we couldn't understand - if token != "," && token != "}" { - print_warning("Parser cannot understand fully the expression of enum member ", member.name, "."); - for token != "," && token != "}" { - eat_token(data); - token = peek_token(data); - } - } - if token == "," { - check_and_eat_token(data, ","); - token = peek_token(data); - } - - append(members, member); - } - - check_and_eat_token(data, "}"); -} - -/** - * { - * <type> <name>; - * <type> <name1>, <name2>; - * <type> <name>[<dimension>]; - * } - */ -parse_struct_or_union_members :: proc(data : ^ParserData, structOrUnionMembers : ^[dynamic]StructOrUnionMember) { - check_and_eat_token(data, "{"); - - // To ensure unique id - unamedCount := 0; - - token := peek_token(data); - for token != "}" { - member : StructOrUnionMember; - member.type = parse_type(data, true); - - for true { - // In the case of function pointer types, the name has been parsed - // during type inspection. - if type, ok := member.type.base.(FunctionPointerType); ok { - member.name = type.name; - } - else { - // Unamed (struct or union) - token = peek_token(data); - if !is_identifier(token) { - member.name = tcat("unamed", unamedCount); - unamedCount += 1; - } - else { - member.name = parse_identifier(data); - } - } - - parse_type_dimensions(data, &member.type); - - token = peek_token(data); - if token == ":" { - check_and_eat_token(data, ":"); - print_warning("Found bitfield in struct, which is not handled correctly."); - evaluate_i64(data); - token = peek_token(data); - } - - append(structOrUnionMembers, member); - - // Multiple declarations on one line - if token == "," { - check_and_eat_token(data, ","); - continue; - } - - break; - } - - check_and_eat_token(data, ";"); - token = peek_token(data); - } - - check_and_eat_token(data, "}"); -} - -parse_variable_or_function_declaration :: proc(data : ^ParserData) { - type := parse_type(data, true); - - // If it's just a type, it might be a struct definition - token := peek_token(data); - if token == ";" { - check_and_eat_token(data, ";"); - return; - } - - // Eat array declaration if any - // @fixme The return type of a function declaration will be wrong! - for data.bytes[data.offset] == '[' { - for data.bytes[data.offset] != ']' { - data.offset += 1; - } - data.offset += 1; - } - - name := parse_identifier(data); - - token = peek_token(data); - if token == "(" { - functionDeclarationNode := parse_function_declaration(data); - functionDeclarationNode.returnType = type; - functionDeclarationNode.name = name; - return; - } else if token == "[" { - // Eat whole array declaration - for data.bytes[data.offset] == '[' { - for data.bytes[data.offset] != ']' { - data.offset += 1; - } - data.offset += 1; - } - } - - // Global variable declaration (with possible multiple declarations) - token = peek_token(data); - - for true { - if token == "," { - print_warning("Found global variable declaration '", name, "', we won't generated any binding for it."); - check_and_eat_token(data, ","); - - name = parse_identifier(data); - token = peek_token(data); - continue; - } - else if token == ";" { - if name != "" { - print_warning("Found global variable declaration '", name, "', we won't generated any binding for it."); - } - check_and_eat_token(data, ";"); - break; - } - - // Global variable assignment, considered as constant define. - node : DefineNode; - - check_and_eat_token(data, "="); - literalValue, ok := evaluate(data); - if ok { - node.name = name; - node.value = literalValue; - append(&data.nodes.defines, node); - } - else { - print_warning("Ignoring global variable expression for '", name, "'."); - } - - name = ""; - token = peek_token(data); - } -} - -parse_function_declaration :: proc(data : ^ParserData) -> ^FunctionDeclarationNode { - node : FunctionDeclarationNode; - - parse_function_parameters(data, &node.parameters); - - // Function definition? Ignore it. - token := peek_token(data); - if token == "{" { - bracesCount := 1; - for true { - data.offset += 1; - if data.bytes[data.offset] == '{' do bracesCount += 1; - else if data.bytes[data.offset] == '}' do bracesCount -= 1; - if bracesCount == 0 do break; - } - data.offset += 1; - } // Function declaration - else { - check_and_eat_token(data, ";"); - } - - append(&data.nodes.functionDeclarations, node); - return &data.nodes.functionDeclarations[len(data.nodes.functionDeclarations) - 1]; -} - -parse_function_parameters :: proc(data : ^ParserData, parameters : ^[dynamic]FunctionParameter) { - check_and_eat_token(data, "("); - - token := peek_token(data); - for token != ")" { - parameter : FunctionParameter; - - token = peek_token(data); - if token == "." { - print_warning("A function accepts variadic arguments, this is currently not handled within generated code."); - - check_and_eat_token(data, "."); - check_and_eat_token(data, "."); - check_and_eat_token(data, "."); - break; - } else { - parameter.type = parse_type(data); - } - - // Check if named parameter - token = peek_token(data); - if token != ")" && token != "," { - parameter.name = parse_identifier(data); - parse_type_dimensions(data, ¶meter.type); - token = peek_token(data); - } - - if token == "," { - eat_token(data); - token = peek_token(data); - } - - append(parameters, parameter); - } - - check_and_eat_token(data, ")"); -} diff --git a/core/bindgen/errors.odin b/core/bindgen/errors.odin deleted file mode 100644 index 9564c5244..000000000 --- a/core/bindgen/errors.odin +++ /dev/null @@ -1,44 +0,0 @@ -package bindgen - -import "core:fmt" -import "core:os" - -seenWarnings : map[string]bool; - -print_warning :: proc(args : ..any) { - message := tcat(..args); - - if !seenWarnings[message] { - fmt.eprint("[bindgen] Warning: ", message, "\n"); - seenWarnings[message] = true; - } -} - -print_error :: proc(data : ^ParserData, loc := #caller_location, args : ..any) { - message := tcat(..args); - - min : u32 = 0; - for i := data.offset - 1; i > 0; i -= 1 { - if data.bytes[i] == '\n' { - min = i + 1; - break; - } - } - - max := min + 200; - for i := min + 1; i < max; i += 1 { - if data.bytes[i] == '\n' { - max = i; - break; - } - } - - line, _ := get_line_column(data); - - fmt.eprint("[bindgen] Error: ", message, "\n"); - fmt.eprint("[bindgen] ... from ", loc.procedure, "\n"); - fmt.eprint("[bindgen] ... at line ", line, " within this context:\n"); - fmt.eprint("> ", extract_string(data, min, max), "\n"); - - os.exit(1); -} diff --git a/core/bindgen/generator-clean.odin b/core/bindgen/generator-clean.odin deleted file mode 100644 index 8dd837b10..000000000 --- a/core/bindgen/generator-clean.odin +++ /dev/null @@ -1,284 +0,0 @@ -package bindgen - -import "core:fmt" - -// Prevent keywords clashes and other tricky cases -clean_identifier :: proc(name : string) -> string { - name := name; - - if name == "" { - return name; - } - - // Starting with _? Try removing that. - for true { - if name[0] == '_' { - name = name[1:]; - } - else { - break; - } - } - - // Number - if name[0] >= '0' && name[0] <= '9' { - return tcat("_", name); - } // Keywords clash - else if name == "map" || name == "proc" || name == "opaque" || name == "in" { - return tcat("_", name); - } // Jai keywords clash - else if name == "context" || - name == "float32" || name == "float64" || - name == "s8" || name == "s16" || name == "s32" || name == "s64" || - name == "u8" || name == "u16" || name == "u32" || name == "u64" { - return tcat("_", name); - } - - return name; -} - -clean_variable_name :: proc(name : string, options : ^GeneratorOptions) -> string { - name := name; - name = change_case(name, options.variableCase); - return clean_identifier(name); -} - -clean_pseudo_type_name :: proc(structName : string, options : ^GeneratorOptions) -> string { - structName := structName; - structName = remove_postfixes(structName, options.pseudoTypePostfixes, options.pseudoTypeTransparentPostfixes); - structName = remove_prefixes(structName, options.pseudoTypePrefixes, options.pseudoTypeTransparentPrefixes); - structName = change_case(structName, options.pseudoTypeCase); - return structName; -} - -// Clean up the enum name so that it can be used to remove the prefix from enum values. -clean_enum_name_for_prefix_removal :: proc(enumName : string, options : ^GeneratorOptions) -> (string, [dynamic]string) { - enumName := enumName; - - if !options.enumValueNameRemove { - return enumName, nil; - } - - // Remove postfix and use same case convention as the enum values - removedPostfixes : [dynamic]string; - enumName, removedPostfixes = remove_postfixes_with_removed(enumName, options.enumValueNameRemovePostfixes); - enumName = change_case(enumName, options.enumValueCase); - return enumName, removedPostfixes; -} - -clean_enum_value_name :: proc(valueName : string, enumName : string, postfixes : []string, options : ^GeneratorOptions) -> string { - valueName := valueName; - - valueName = remove_prefixes(valueName, options.enumValuePrefixes, options.enumValueTransparentPrefixes); - valueName = remove_postfixes(valueName, postfixes, options.enumValueTransparentPostfixes); - - if options.enumValueNameRemove { - valueName = remove_prefixes(valueName, []string{enumName}); - } - - valueName = change_case(valueName, options.enumValueCase); - - return clean_identifier(valueName); -} - -clean_function_name :: proc(functionName : string, options : ^GeneratorOptions) -> string { - functionName := functionName; - functionName = remove_prefixes(functionName, options.functionPrefixes, options.functionTransparentPrefixes); - functionName = remove_postfixes(functionName, options.definePostfixes, options.defineTransparentPostfixes); - functionName = change_case(functionName, options.functionCase); - return functionName; -} - -clean_define_name :: proc(defineName : string, options : ^GeneratorOptions) -> string { - defineName := defineName; - defineName = remove_prefixes(defineName, options.definePrefixes, options.defineTransparentPrefixes); - defineName = remove_postfixes(defineName, options.definePostfixes, options.defineTransparentPostfixes); - defineName = change_case(defineName, options.defineCase); - return defineName; -} - -// Convert to Odin's types -clean_type :: proc(data : ^GeneratorData, type : Type, baseTab : string = "", explicitSharpType := true) -> string { - output := ""; - - for dimension in type.dimensions { - output = tcat(output, "[", dimension, "]"); - } - output = tcat(output, clean_base_type(data, type.base, baseTab, explicitSharpType)); - - return output; -} - -clean_base_type :: proc(data : ^GeneratorData, baseType : BaseType, baseTab : string = "", explicitSharpType := true) -> string { - options := data.options; - - if _type, ok := baseType.(BuiltinType); ok { - if _type == BuiltinType.Void do return options.mode == "jai" ? "void" : ""; - else if _type == BuiltinType.Int do return options.mode == "jai" ? "s64" : "_c.int"; - else if _type == BuiltinType.UInt do return options.mode == "jai" ? "u64" :"_c.uint"; - else if _type == BuiltinType.LongInt do return options.mode == "jai" ? "s64" :"_c.long"; - else if _type == BuiltinType.ULongInt do return options.mode == "jai" ? "u64" :"_c.ulong"; - else if _type == BuiltinType.LongLongInt do return options.mode == "jai" ? "s64" :"_c.longlong"; - else if _type == BuiltinType.ULongLongInt do return options.mode == "jai" ? "u64" :"_c.ulonglong"; - else if _type == BuiltinType.ShortInt do return options.mode == "jai" ? "s16" :"_c.short"; - else if _type == BuiltinType.UShortInt do return options.mode == "jai" ? "u16" :"_c.ushort"; - else if _type == BuiltinType.Char do return options.mode == "jai" ? "u8" :"_c.char"; - else if _type == BuiltinType.SChar do return options.mode == "jai" ? "s8" :"_c.schar"; - else if _type == BuiltinType.UChar do return options.mode == "jai" ? "u8" :"_c.uchar"; - else if _type == BuiltinType.Float do return options.mode == "jai" ? "float32" :"_c.float"; - else if _type == BuiltinType.Double do return options.mode == "jai" ? "float64" :"_c.double"; - else if _type == BuiltinType.LongDouble { - print_warning("Found long double which is currently not supported. Fallback to double in generated code."); - return options.mode == "jai" ? "double" :"_c.double"; - } - else if _type == BuiltinType.Int8 do return options.mode == "jai" ? "s8" :"i8"; - else if _type == BuiltinType.Int16 do return options.mode == "jai" ? "s16" :"i16"; - else if _type == BuiltinType.Int32 do return options.mode == "jai" ? "s32" :"i32"; - else if _type == BuiltinType.Int64 do return options.mode == "jai" ? "s64" :"i64"; - else if _type == BuiltinType.UInt8 do return options.mode == "jai" ? "u8" :"u8"; - else if _type == BuiltinType.UInt16 do return options.mode == "jai" ? "u16" :"u16"; - else if _type == BuiltinType.UInt32 do return options.mode == "jai" ? "u32" :"u32"; - else if _type == BuiltinType.UInt64 do return options.mode == "jai" ? "u64" :"u64"; - else if _type == BuiltinType.Size do return options.mode == "jai" ? "u64" :"_c.size_t"; - else if _type == BuiltinType.SSize do return options.mode == "jai" ? "u64" :"_c.ssize_t"; - else if _type == BuiltinType.PtrDiff do return options.mode == "jai" ? "s64" :"_c.ptrdiff_t"; - else if _type == BuiltinType.UIntPtr do return options.mode == "jai" ? "u64" :"_c.uintptr_t"; - else if _type == BuiltinType.IntPtr do return options.mode == "jai" ? "s64" :"_c.intptr_t"; - } - else if _type, ok := baseType.(PointerType); ok { - if options.mode == "jai" { - // Hide pointers to types that were not declared. - if !is_known_base_type(data, _type.type.base) { - print_warning("*", _type.type.base.(IdentifierType).name, " replaced by *void as the pointed type is unknown."); - return "*void"; - } - } else { - if __type, ok := _type.type.base.(BuiltinType); ok { - if __type == BuiltinType.Void do return "rawptr"; - else if __type == BuiltinType.Char do return "cstring"; - } - } - name := clean_type(data, _type.type^, baseTab); - return tcat(options.mode == "jai" ? "*" :"^", name); - } - else if _type, ok := baseType.(IdentifierType); ok { - return clean_pseudo_type_name(_type.name, options); - } - else if _type, ok := baseType.(FunctionType); ok { - output : string; - if explicitSharpType { - output = "#type "; - } - output = tcat(output, options.mode == "jai" ? "(" :"proc("); - parameters := clean_function_parameters(data, _type.parameters, baseTab); - output = tcat(output, parameters, ")"); - - returnType := clean_type(data, _type.returnType^); - if len(returnType) > 0 && returnType != "void" { - output = tcat(output, " -> ", returnType); - } - return output; - } - else if _type, ok := baseType.(FunctionPointerType); ok { - output : string; - if explicitSharpType { - output = "#type "; - } - output = tcat(output, options.mode == "jai" ? "(" :"proc("); - parameters := clean_function_parameters(data, _type.parameters, baseTab); - output = tcat(output, parameters, ")"); - - returnType := clean_type(data, _type.returnType^); - if len(returnType) > 0 && returnType != "void" { - output = tcat(output, " -> ", returnType); - } - - if options.mode == "jai" { - output = tcat(output, " #foreign"); - } - return output; - } - - return "<niy>"; -} - -clean_function_parameters :: proc(data : ^GeneratorData, parameters : [dynamic]FunctionParameter, baseTab : string) -> string { - output := ""; - options := data.options; - - // Special case: function(void) does not really have a parameter - if len(parameters) == 1 { - if _type, ok := parameters[0].type.base.(BuiltinType); ok { - if _type == BuiltinType.Void { - return ""; - } - } - } - - tab := ""; - if options.mode == "jai" { // @note :OdinCodingStyle Odin forces a coding style, now. Ugh. - if (len(parameters) > 1) { - output = tcat(output, "\n"); - tab = tcat(baseTab, " "); - } - } - - unamedParametersCount := 0; - for parameter, i in parameters { - type := clean_type(data, parameter.type); - - name : string; - if len(parameter.name) != 0 { - name = clean_variable_name(parameter.name, options); - } else { - name = tcat("unamed", unamedParametersCount); - unamedParametersCount += 1; - } - - output = tcat(output, tab, name, " : ", type); - - if i != len(parameters) - 1 { - if options.mode == "jai" { // @note :OdinCodingStyle - output = tcat(output, ",\n"); - } else { - output = tcat(output, ", "); - } - } - } - - if (len(parameters) > 1) { - if options.mode == "jai" { // @note :OdinCodingStyle - output = tcat(output, "\n", baseTab); - } - } - - return output; -} - -is_known_base_type :: proc(data : ^GeneratorData, baseType : BaseType) -> bool { - if _type, ok := baseType.(IdentifierType); ok { - for it in data.nodes.typedefs { - if _type.name == it.name { - return true; - } - } - for it in data.nodes.structDefinitions { - if _type.name == it.name { - return true; - } - } - for it in data.nodes.enumDefinitions { - if _type.name == it.name { - return true; - } - } - for it in data.nodes.unionDefinitions { - if _type.name == it.name { - return true; - } - } - return false; - } - - return true; -} diff --git a/core/bindgen/generator-export.odin b/core/bindgen/generator-export.odin deleted file mode 100644 index a04113ed9..000000000 --- a/core/bindgen/generator-export.odin +++ /dev/null @@ -1,166 +0,0 @@ -package bindgen - -import "core:os" -import "core:fmt" - -export_defines :: proc(data : ^GeneratorData) { - for node in data.nodes.defines { - defineName := clean_define_name(node.name, data.options); - - // @fixme fprint of float numbers are pretty badly handled, - // just has a 10^-3 precision. - fcat(data.handle, defineName, " :: ", node.value, ";\n"); - } - fcat(data.handle, "\n"); -} - -export_typedefs :: proc(data : ^GeneratorData) { - for node in data.nodes.typedefs { - name := clean_pseudo_type_name(node.name, data.options); - type := clean_type(data, node.type, "", true); - if name == type do continue; - fcat(data.handle, name, " :: ", type, ";\n"); - } - fcat(data.handle, "\n"); -} - -export_enums :: proc(data : ^GeneratorData) { - for node in data.nodes.enumDefinitions { - enumName := clean_pseudo_type_name(node.name, data.options); - - if data.options.mode == "jai" { - consideredFlags := false; - for postfix in data.options.enumConsideredFlagsPostfixes { - if ends_with(node.name, postfix) { - consideredFlags = true; - break; - } - } - - if consideredFlags { - fcat(data.handle, enumName, " :: enum_flags u32 {"); - } else { - fcat(data.handle, enumName, " :: enum s32 {"); - } - } else { - fcat(data.handle, enumName, " :: enum i32 {"); - } - - postfixes : [dynamic]string; - enumName, postfixes = clean_enum_name_for_prefix_removal(enumName, data.options); - - // Changing the case of postfixes to the enum value one, - // so that they can be removed. - enumValueCase := find_case(node.members[0].name); - for postfix, i in postfixes { - postfixes[i] = change_case(postfix, enumValueCase); - } - - // And changing the case of enumName to the enum value one - enumName = change_case(enumName, enumValueCase); - - // Merging enum value postfixes with postfixes that have been removed from the enum name. - for postfix in data.options.enumValuePostfixes { - append(&postfixes, postfix); - } - - export_enum_members(data, node.members, enumName, postfixes[:]); - fcat(data.handle, data.options.mode == "jai" ? "}\n" : "};\n"); - fcat(data.handle, "\n"); - } -} - -export_structs :: proc(data : ^GeneratorData) { - for node in data.nodes.structDefinitions { - structName := clean_pseudo_type_name(node.name, data.options); - fcat(data.handle, structName, " :: struct {"); - export_struct_or_union_members(data, node.members); - fcat(data.handle, data.options.mode == "jai" ? "}\n" : "};\n"); - fcat(data.handle, "\n"); - } -} - -export_unions :: proc(data : ^GeneratorData) { - for node in data.nodes.unionDefinitions { - unionName := clean_pseudo_type_name(node.name, data.options); - fcat(data.handle, unionName, data.options.mode == "jai" ? " :: union {" : " :: struct #raw_union {"); - export_struct_or_union_members(data, node.members); - fcat(data.handle, data.options.mode == "jai" ? "}\n" : "};\n"); - fcat(data.handle, "\n"); - } -} - -export_functions :: proc(data : ^GeneratorData) { - for node in data.nodes.functionDeclarations { - functionName := clean_function_name(node.name, data.options); - if data.options.mode == "jai" { - fcat(data.handle, functionName, " :: ("); - } else { - fcat(data.handle, " @(link_name=\"", node.name, "\")\n"); - fcat(data.handle, " ", functionName, " :: proc("); - } - parameters := clean_function_parameters(data, node.parameters, data.options.mode == "jai" ? "" : " "); - fcat(data.handle, parameters, ")"); - returnType := clean_type(data, node.returnType); - if len(returnType) > 0 { - fcat(data.handle, " -> ", returnType); - } - if data.options.mode == "jai" { - fcat(data.handle, " #foreign ", data.foreignLibrary, " \"", node.name ,"\";\n"); - } else { - fcat(data.handle, " ---;\n"); - } - fcat(data.handle, "\n"); - } -} - -export_enum_members :: proc(data : ^GeneratorData, members : [dynamic]EnumMember, enumName : string, postfixes : []string) { - if (len(members) > 0) { - fcat(data.handle, "\n"); - } - - cleanedMembers : [dynamic]EnumMember; - for member in members { - cleanedMember : EnumMember; - cleanedMember.hasValue = member.hasValue; - cleanedMember.value = member.value; - cleanedMember.name = clean_enum_value_name(member.name, enumName, postfixes, data.options); - - if len(cleanedMember.name) == 0 { - // print_warning("Enum member ", member.name, " resolves to an empty name. Ignoring it."); - continue; - } - - // Ensuring that we don't collide with an other enum member. - foundCopy := false; - for existingCleanedMember in cleanedMembers { - if cleanedMember.name == existingCleanedMember.name && - cleanedMember.hasValue == existingCleanedMember.hasValue && - cleanedMember.value == existingCleanedMember.value { - print_warning("Enum member ", member.name, " is duplicated once cleaned. Keeping only one copy."); - foundCopy = true; - break; - } - } - if foundCopy do continue; - - fcat(data.handle, " ", cleanedMember.name); - if member.hasValue { - fcat(data.handle, data.options.mode == "jai" ? " :: " : " = ", member.value); - } - fcat(data.handle, data.options.mode == "jai" ? ";\n" : ",\n"); - - append(&cleanedMembers, cleanedMember); - } -} - -export_struct_or_union_members :: proc(data : ^GeneratorData, members : [dynamic]StructOrUnionMember) { - if (len(members) > 0) { - fcat(data.handle, "\n"); - } - for member in members { - type := clean_type(data, member.type, " "); - name := clean_variable_name(member.name, data.options); - fcat(data.handle, " ", name, " : ", type, data.options.mode == "jai" ? ";\n" : ",\n"); - } -} diff --git a/core/bindgen/generator-helpers.odin b/core/bindgen/generator-helpers.odin deleted file mode 100644 index a3b37f4f6..000000000 --- a/core/bindgen/generator-helpers.odin +++ /dev/null @@ -1,392 +0,0 @@ -package bindgen - -import "core:fmt" -import "core:os" -import "core:io" -import "core:strings" -import "core:unicode/utf8" - -Case :: enum { - Unknown, - Camel, - Constant, - Kebab, - Pascal, - Snake, -} - -WordCase :: enum { - Unknown, - Up, - Low, - FirstUp, - // When first upping, numbers are followed always by a capital - FirstUpNumberReset, -} - -// Change a character to a capital. -to_uppercase :: proc(c : rune) -> rune { - c := c; - if c >= 'a' && c <= 'z' { - c = c - 'a' + 'A'; - } - return c; -} - -// Change a character to lowercase. -to_lowercase :: proc(c : rune) -> rune { - c := c; - if c >= 'A' && c <= 'Z' { - c = c - 'A' + 'a'; - } - return c; -} - -// @note Stolen tprint and fprint from fmt package, because it was confusing due to args: ..any and sep default parameter. -tcat :: proc(args: ..any) -> string { - return fmt.tprint(args=args, sep=""); -} - -fcat :: proc(fd: os.Handle, args: ..any) -> int { - return fmt.fprint(fd=fd, args=args, sep=""); -} - -// Change the case convention of a word. -change_word_case :: proc(str : string, targetCase : WordCase) -> string { - newStr : string; - if targetCase == WordCase.Up { - for c in str { - newStr = tcat(newStr, to_uppercase(c)); - } - } - else if targetCase == WordCase.Low { - for c in str { - newStr = tcat(newStr, to_lowercase(c)); - } - } - else if targetCase == WordCase.FirstUp { - for c, i in str { - if i == 0 { - newStr = tcat(newStr, to_uppercase(c)); - } else { - newStr = tcat(newStr, to_lowercase(c)); - } - } - } - else if targetCase == WordCase.FirstUpNumberReset { - for c, i in str { - if i == 0 || (str[i - 1] >= '0' && str[i - 1] <= '9') { - newStr = tcat(newStr, to_uppercase(c)); - } else { - newStr = tcat(newStr, to_lowercase(c)); - } - } - } - return newStr; -} - -// Change the case convention of a string by detecting original convention, -// then splitting it into words. -change_case :: proc(str : string, targetCase : Case) -> string { - if targetCase == Case.Unknown { - return str; - } - - // Split - parts := autosplit_string(str); - - // Join - newStr : string; - if targetCase == Case.Pascal { - for part, i in parts { - newStr = tcat(newStr, change_word_case(part, WordCase.FirstUpNumberReset)); - } - } - else if targetCase == Case.Snake { - for part, i in parts { - newStr = tcat(newStr, change_word_case(part, WordCase.Low), (i != len(parts) - 1) ? "_" : ""); - } - } - else if targetCase == Case.Kebab { - for part, i in parts { - newStr = tcat(newStr, change_word_case(part, WordCase.Low), (i != len(parts) - 1) ? "-" : ""); - } - } - else if targetCase == Case.Camel { - for part, i in parts { - if i == 0 { - newStr = tcat(newStr, change_word_case(part, WordCase.Low)); - } else { - newStr = tcat(newStr, change_word_case(part, WordCase.FirstUpNumberReset)); - } - } - } - else if targetCase == Case.Constant { - for part, i in parts { - newStr = tcat(newStr, change_word_case(part, WordCase.Up), (i != len(parts) - 1) ? "_" : ""); - } - } - - return newStr; -} - -// Identify the case of the provided string. -// Full lowercase with no separator is identified as camelCase. -find_case :: proc(str : string) -> Case { - refuted : bool; - - // CONSTANT_CASE - refuted = false; - for c in str { - if (c != '_') && (c < 'A' || c > 'Z') && (c < '0' || c > '9') { - refuted = true; - break; - } - } - if !refuted do return Case.Constant; - - for c in str { - // snake_case - if c == '_' { - return Case.Snake; - } // kebab-case - else if c == '-' { - return Case.Kebab; - } - } - - // PascalCase - if str[0] >= 'A' && str[0] <= 'Z' { - return Case.Pascal; - } - - // camelCase - return Case.Camel; -} - -// Splits the string according to detected case. -// HeyBuddy -> {"Hey", "Buddy"} -// hey-buddy -> {"hey", "buddy"} -// _hey_buddy -> {"", "hey", "buddy"} -// and such... -autosplit_string :: proc(str : string) -> [dynamic]string { - lowCount := 0; - upCount := 0; - for c in str { - // If any '_', split according to that (CONSTANT_CASE or snake_case) - if c == '_' { - return split_from_separator(str, '_'); - } // If any '-', split according to that (kebab-case) - else if c == '-' { - return split_from_separator(str, '-'); - } - else if c >= 'a' && c <= 'z' { - lowCount += 1; - } - else if c >= 'A' && c <= 'Z' { - upCount += 1; - } - } - - // If it seems to be only one word - if lowCount == 0 || upCount == 0 { - parts : [dynamic]string; - append(&parts, str); - return parts; - } - - // Split at each uppercase letter (PascalCase or camelCase) - return split_from_capital(str); -} - -split_from_separator :: proc(str : string, sep : rune) -> [dynamic]string { - parts : [dynamic]string; - - lastI := 0; - - // Empty strings for starting separators in string - for c in str { - if c == sep { - append(&parts, ""); - lastI += 1; - } else { - break; - } - } - - // Ignore non letter prefix - if lastI == 0 { - for c in str { - if (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') { - lastI += 1; - } - else { - break; - } - } - } - - for c, i in str { - if i > lastI + 1 && c == sep { - append(&parts, str[lastI:i]); - lastI = i + 1; - } - } - - append(&parts, str[lastI:]); - - return parts; -} - -split_from_capital :: proc(str : string) -> [dynamic]string { - parts : [dynamic]string; - - // Ignore non letter prefix - lastI := 0; - for c in str { - if (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') { - lastI += 1; - } - else { - break; - } - } - - // We want to handle: - // myBrainIsCRAZY -> my Brain Is Crazy - // myCRAZYBrain -> my CRAZY Brain - // SOLO -> SOLO - - // Do split - for i := 1; i < len(str); i += 1 { - if str[i] >= 'A' && str[i] <= 'Z' { - // Do not split too much if it seems to be a capitalized word - if (lastI == i - 1) && (str[lastI] >= 'A' && str[lastI] <= 'Z') { - for ; i + 1 < len(str); i += 1 { - if str[i + 1] < 'A' || str[i + 1] > 'Z' { - break; - } - } - if (i + 1 == len(str)) && (str[i] >= 'A' && str[i] <= 'Z') { - i += 1; - } - } - - append(&parts, str[lastI:i]); - lastI = i; - } - } - - if lastI != len(str) { - append(&parts, str[lastI:]); - } - - return parts; -} - -// Check if str if prefixed with any of the provided strings, -// even combinaisons of those, and remove them. -remove_prefixes :: proc(str : string, prefixes : []string, transparentPrefixes : []string = nil) -> string { - str := str; - transparentStr := ""; - - found := true; - for found { - found = false; - - // Remove effective prefixes - for prefix in prefixes { - if len(str) >= len(prefix) && - str[:len(prefix)] == prefix { - str = str[len(prefix):]; - if len(str) != 0 && (str[0] == '_' || str[0] == '-') { - str = str[1:]; - } - found = true; - break; - } - } - - if found do continue; - - // Remove transparent ones, only one by one, - // as we want effective ones to be fully removed. - for prefix in transparentPrefixes { - if len(str) >= len(prefix) && - str[:len(prefix)] == prefix { - str = str[len(prefix):]; - transparentStr = tcat(transparentStr, prefix); - if len(str) != 0 && (str[0] == '_' || str[0] == '-') { - str = str[1:]; - transparentStr = tcat(transparentStr, '_'); - } - found = true; - break; - } - } - } - - return tcat(transparentStr, str); -} - -// Check if str if postfixes with any of the provided strings, -// even combinaisons of those, and remove them. -remove_postfixes_with_removed :: proc( - str : string, - postfixes : []string, - transparentPostfixes : []string = nil) -> (string, [dynamic]string) { - str := str; - removedPostfixes : [dynamic]string; - transparentStr := ""; - - found := true; - for found { - found = false; - - // Remove effective postfixes - for postfix in postfixes { - if ends_with(str, postfix) { - str = str[:len(str) - len(postfix)]; - if len(str) != 0 && (str[len(str)-1] == '_' || str[len(str)-1] == '-') { - str = str[:len(str)-1]; - } - append(&removedPostfixes, postfix); - found = true; - break; - } - } - - if found do continue; - - // Remove transparent ones, only one by one, - // as we want effective ones to be fully removed. - for postfix in transparentPostfixes { - if ends_with(str, postfix) { - str = str[:len(str) - len(postfix)]; - transparentStr = tcat(postfix, transparentStr); - if len(str) != 0 && (str[len(str)-1] == '_' || str[len(str)-1] == '-') { - str = str[:len(str)-1]; - transparentStr = tcat('_', transparentStr); - } - found = true; - break; - } - } - } - - return tcat(str, transparentStr), removedPostfixes; -} - -remove_postfixes :: proc( - str : string, - postfixes : []string, - transparentPostfixes : []string = nil) -> string { - str := str; - removedPostfixes : [dynamic]string; - str, removedPostfixes = remove_postfixes_with_removed(str, postfixes, transparentPostfixes); - return str; -} - -ends_with :: proc(str : string, postfix : string) -> bool { - return len(str) >= len(postfix) && str[len(str) - len(postfix):] == postfix; -} diff --git a/core/bindgen/generator.odin b/core/bindgen/generator.odin deleted file mode 100644 index 3ef3d69c0..000000000 --- a/core/bindgen/generator.odin +++ /dev/null @@ -1,205 +0,0 @@ -/** - * Odin binding generator from C header data. - */ - -package bindgen - -import "core:os" -import "core:fmt" -import "core:runtime" - -GeneratorOptions :: struct { - mode : string, // "odin" or "jai" - - // Variable - variableCase : Case, - - // Defines - definePrefixes : []string, - defineTransparentPrefixes : []string, - definePostfixes : []string, - defineTransparentPostfixes : []string, - defineCase : Case, - - // Pseudo-types - pseudoTypePrefixes : []string, - pseudoTypeTransparentPrefixes : []string, - pseudoTypePostfixes : []string, - pseudoTypeTransparentPostfixes : []string, - pseudoTypeCase : Case, - - // Enums - enumConsideredFlagsPostfixes : []string, - - // Functions - functionPrefixes : []string, - functionTransparentPrefixes : []string, - functionPostfixes : []string, - functionTransparentPostfixes : []string, - functionCase : Case, - - // Enum values - enumValuePrefixes : []string, - enumValueTransparentPrefixes : []string, - enumValuePostfixes : []string, - enumValueTransparentPostfixes : []string, - enumValueCase : Case, - enumValueNameRemove : bool, - enumValueNameRemovePostfixes : []string, - - parserOptions : ParserOptions, -} - -GeneratorData :: struct { - handle : os.Handle, - nodes : Nodes, - - // References - foreignLibrary : string, - options : ^GeneratorOptions, -} - -generate :: proc( - packageName : string, - foreignLibrary : string, - outputFile : string, - headerFiles : []string, - options : GeneratorOptions, -) { - options := options; - data : GeneratorData; - data.options = &options; - data.foreignLibrary = foreignLibrary; - - if options.mode == "" { - options.mode = "odin"; - } - - // Outputing odin file - errno : os.Errno; - - // chmod 664 when creating file - mode: int = 0; - when os.OS == "linux" || os.OS == "darwin" { - mode = os.S_IRUSR | os.S_IWUSR | os.S_IRGRP | os.S_IWGRP | os.S_IROTH; - } - - data.handle, errno = os.open(outputFile, os.O_WRONLY | os.O_CREATE | os.O_TRUNC, mode); - if errno != 0 { - fmt.eprint("[bindgen] Unable to write to output file ", outputFile, " (", errno ,")\n"); - return; - } - defer os.close(data.handle); - - if options.mode == "jai" { - fcat(data.handle, foreignLibrary, " :: #foreign_library \"", foreignLibrary, "\";\n"); - fcat(data.handle, "\n"); - } else { - fcat(data.handle, "package ", packageName, "\n"); - fcat(data.handle, "\n"); - fcat(data.handle, "foreign import \"", foreignLibrary, "\"\n"); - fcat(data.handle, "\n"); - fcat(data.handle, "import _c \"core:c\"\n"); - fcat(data.handle, "\n"); - } - - // Parsing header files - anonymousStructCount = 0; - anonymousUnionCount = 0; - anonymousEnumCount = 0; - - for headerFile in headerFiles { - bytes, ok := os.read_entire_file(headerFile); - if !ok { - fmt.eprint("[bindgen] Unable to read file ", headerFile, "\n"); - return; - } - - // We fuse the SOAs - headerNodes := parse(bytes, options.parserOptions); - merge_generic_nodes(&data.nodes.defines, &headerNodes.defines); - merge_generic_nodes(&data.nodes.enumDefinitions, &headerNodes.enumDefinitions); - merge_generic_nodes(&data.nodes.unionDefinitions, &headerNodes.unionDefinitions); - merge_forward_declared_nodes(&data.nodes.structDefinitions, &headerNodes.structDefinitions); - merge_generic_nodes(&data.nodes.functionDeclarations, &headerNodes.functionDeclarations); - merge_generic_nodes(&data.nodes.typedefs, &headerNodes.typedefs); - } - - // Exporting - export_defines(&data); - export_typedefs(&data); - export_enums(&data); - export_structs(&data); - export_unions(&data); - - // Foreign block for functions - if options.mode != "jai" { - foreignLibrarySimple := simplify_library_name(foreignLibrary); - fcat(data.handle, "@(default_calling_convention=\"c\")\n"); - fcat(data.handle, "foreign ", foreignLibrarySimple, " {\n"); - fcat(data.handle, "\n"); - } - - export_functions(&data); - - if options.mode != "jai" { - fcat(data.handle, "}\n"); - } -} - -// system:foo.lib -> foo -simplify_library_name :: proc(libraryName : string) -> string { - startOffset := 0; - endOffset := len(libraryName); - - for c, i in libraryName { - if startOffset == 0 && c == ':' { - startOffset = i + 1; - } - else if c == '.' { - endOffset = i; - break; - } - } - - return libraryName[startOffset:endOffset]; -} - -merge_generic_nodes :: proc(nodes : ^$T, headerNodes : ^T) { - for headerNode in headerNodes { - // Check that there are no duplicated nodes (due to forward declaration or such) - duplicatedIndex := -1; - for i := 0; i < len(nodes); i += 1 { - node := nodes[i]; - if node.name == headerNode.name { - duplicatedIndex = i; - break; - } - } - - if duplicatedIndex < 0 { - append(nodes, headerNode); - } - } -} - -merge_forward_declared_nodes :: proc(nodes : ^$T, headerNodes : ^T) { - for headerNode in headerNodes { - // Check that there are no duplicated nodes (due to forward declaration or such) - duplicatedIndex := -1; - for i := 0; i < len(nodes); i += 1 { - node := nodes[i]; - if node.name == headerNode.name { - duplicatedIndex = i; - break; - } - } - - if duplicatedIndex < 0 { - append(nodes, headerNode); - } - else if !headerNode.forwardDeclared && len(headerNode.members) > 0 { - nodes[duplicatedIndex] = headerNode; - } - } -} diff --git a/wasm-ld b/wasm-ld deleted file mode 120000 index 01ef2a7e7..000000000 --- a/wasm-ld +++ /dev/null @@ -1 +0,0 @@ -/Volumes/Phill_Backup/pers/programming/sdk/emsdk/upstream/bin/lld
\ No newline at end of file |