rayoko/src/comp_ctx.zig

117 lines
3.5 KiB
Zig

const std = @import("std");
pub const CompilationError = error{TypeError};
pub const SymbolTable = std.hash_map.StringHashMap(SymbolData);
pub const SymbolUnderlyingTypeEnum = enum {
Integer32,
Integer64,
Bool,
CustomType,
};
pub const SymbolUnderlyingType = union(SymbolUnderlyingTypeEnum) {
Integer32: void,
Integer64: void,
Bool: void,
CustomType: []const u8,
};
// functions, for our purposes, other than symbols, have:
// - a return type
// - TODO parameters
pub const FunctionSymbol = struct {
return_type: SymbolUnderlyingType,
/// Parameters for a function are also a table instead of an ArrayList
/// because we want to resolve identifiers to them.
parameters: SymbolTable,
symbols: SymbolTable,
/// Find a given identifier in the function. Can resolve to either a parameter
pub fn findSymbol(self: *const FuncionSymbol, identifier: []const u8) ?Symbol {
// try to find it on overall variable symbols
var var_sym = self.symbols.get(identifier);
if (var_sym != null) return var_sym;
var param_sym = self.parameters.get(identifier);
if (param_sym != null) return param_sym;
return null;
}
};
// structs are hashmaps pointing to SymbolUnderlyingType
pub const UnderlyingTypeMap = std.hash_map.StringHashMap(SymbolUnderlyingType);
// enums have lists of identifiers
pub const IdentifierList = std.ArrayList([]const u8);
// TODO const
pub const SymbolType = enum {
Function,
Struct,
Enum,
Variable,
};
pub const SymbolData = union(SymbolType) {
Function: FunctionSymbol,
Struct: UnderlyingTypeMap,
Enum: IdentifierList,
// variables (parameters and variables), for the type system
// only have types
Variable: SymbolUnderlyingType,
};
const builtin_type_identifiers = [_][]const u8{ "i32", "i64", "bool" };
const builtin_types = [_]SymbolUnderlyingTypeEnum{ .Integer32, .Integer64, .Bool };
/// Represents the context for a full compiler run.
/// This is used to manage the symbol table for the compilation unit, etc.
pub const CompilationContext = struct {
allocator: *std.mem.Allocator,
symbol_table: SymbolTable,
pub fn init(allocator: *std.mem.Allocator) CompilationContext {
return CompilationContext{
.allocator = allocator,
.symbol_table = SymbolTable.init(allocator),
};
}
/// Solve a given type as a string into a SymbolUnderlyingTypeEnum
/// This does not help if you want a full SymbolUnderlyingType, use
/// solveType() for that.
pub fn solveTypeEnum(
self: *@This(),
typ_ident: []const u8,
) SymbolUnderlyingTypeEnum {
inline for (builtin_type_identifiers) |typ, idx| {
if (std.mem.eql(u8, typ, typ_ident)) return builtin_types[idx];
}
return .CustomType;
}
/// Solve a given type string into a full fleged SymbolUnderlyingType.
/// This always works, since resolution into struct/enum custom types
/// become the fallback.
pub fn solveType(
self: *@This(),
typ_ident: []const u8,
) SymbolUnderlyingType {
const typ_enum_val = self.solveTypeEnum(typ_ident);
return switch (typ_enum_val) {
.Integer32 => SymbolUnderlyingType{ .Integer32 = {} },
.Integer64 => SymbolUnderlyingType{ .Integer64 = {} },
.Bool => SymbolUnderlyingType{ .Bool = {} },
.CustomType => SymbolUnderlyingType{ .CustomType = typ_ident },
};
}
};