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