rayoko/src/comp_ctx.zig

163 lines
4.8 KiB
Zig

const std = @import("std");
const ast = @import("ast.zig");
pub const CompilationError = error{TypeError};
pub const SymbolTable = std.hash_map.StringHashMap(SymbolData);
pub const TypeList = std.ArrayList(SymbolUnderlyingType);
pub const SymbolUnderlyingTypeEnum = enum {
Integer32,
Integer64,
Bool,
// opaque unsolved identifier
OpaqueType,
// solved custom type
Struct,
Enum,
};
pub const SymbolUnderlyingType = union(SymbolUnderlyingTypeEnum) {
Integer32: void,
Integer64: void,
Bool: void,
OpaqueType: []const u8,
Struct: []const u8,
Enum: []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: UnderlyingTypeMap,
symbols: SymbolTable,
/// Find a given identifier in the function. Can resolve to either a parameter
pub fn findSymbol(self: *const @This(), identifier: []const u8) ?SymbolData {
// try to find it on overall variable symbols
// TODO
//var var_sym = self.symbols.get(identifier);
//if (var_sym != null) return var_sym.?.value;
//var param_sym = self.parameters.get(identifier);
//if (param_sym != null) return param_sym.?.value;
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 .OpaqueType;
}
/// 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 = {} },
.OpaqueType => SymbolUnderlyingType{ .OpaqueType = typ_ident },
else => unreachable,
};
}
pub fn insertStruct(self: *@This(), struc: ast.Struct, field_types: TypeList) !void {
var type_map = UnderlyingTypeMap.init(self.allocator);
for (struc.fields.toSlice()) |field, idx| {
_ = try type_map.put(field.name.lexeme, field_types.at(idx));
}
_ = try self.symbol_table.put(struc.name.lexeme, SymbolData{ .Struct = type_map });
}
pub fn insertFn(
self: *@This(),
decl: ast.FnDecl,
ret_type: SymbolUnderlyingType,
param_types: TypeList,
symbols: SymbolTable,
) !void {
var type_map = UnderlyingTypeMap.init(self.allocator);
for (decl.params.toSlice()) |param, idx| {
_ = try type_map.put(param.name.lexeme, param_types.at(idx));
}
_ = try self.symbol_table.put(decl.func_name.lexeme, SymbolData{
.Function = FunctionSymbol{
.return_type = ret_type,
.parameters = type_map,
.symbols = symbols,
},
});
}
};