mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-17 03:12:22 -07:00
841 lines
25 KiB
Odin
841 lines
25 KiB
Odin
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, ")");
|
|
}
|