Compare commits
No commits in common. "a081de93ce2bfbe621f82b4418f949522a71101d" and "2cb328c3f9bb17193f6805cf641053cccc71cd17" have entirely different histories.
a081de93ce
...
2cb328c3f9
8 changed files with 38 additions and 112 deletions
|
@ -7,8 +7,8 @@ fn f() i32 {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn f2() i64 {
|
fn f2() i32 {
|
||||||
return 1301;
|
return f() + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum B {
|
enum B {
|
||||||
|
|
|
@ -83,10 +83,7 @@ pub const UnaryExpr = struct {
|
||||||
|
|
||||||
pub const LiteralExpr = union(enum) {
|
pub const LiteralExpr = union(enum) {
|
||||||
Bool: bool,
|
Bool: bool,
|
||||||
|
Integer: []const u8,
|
||||||
Integer32: i32,
|
|
||||||
Integer64: i64,
|
|
||||||
|
|
||||||
Float: []const u8,
|
Float: []const u8,
|
||||||
String: []const u8,
|
String: []const u8,
|
||||||
Array: ExprList,
|
Array: ExprList,
|
||||||
|
|
|
@ -227,8 +227,7 @@ pub fn printExpr(expr: *const Expr) void {
|
||||||
.Literal => |literal| {
|
.Literal => |literal| {
|
||||||
switch (literal) {
|
switch (literal) {
|
||||||
.Bool => |val| std.debug.warn("{}", val),
|
.Bool => |val| std.debug.warn("{}", val),
|
||||||
.Integer32 => |val| std.debug.warn("{}", val),
|
.Integer => |val| std.debug.warn("{}", val),
|
||||||
.Integer64 => |val| std.debug.warn("{}", val),
|
|
||||||
.Float => |val| std.debug.warn("{}", val),
|
.Float => |val| std.debug.warn("{}", val),
|
||||||
.String => |val| std.debug.warn("'{}'", val),
|
.String => |val| std.debug.warn("'{}'", val),
|
||||||
.Array => |exprs| {
|
.Array => |exprs| {
|
||||||
|
@ -363,7 +362,6 @@ fn prettyType(typ: SymbolUnderlyingType) []const u8 {
|
||||||
.Integer32 => "i32",
|
.Integer32 => "i32",
|
||||||
.Integer64 => "i64",
|
.Integer64 => "i64",
|
||||||
.Bool => "bool",
|
.Bool => "bool",
|
||||||
.Double => "double",
|
|
||||||
|
|
||||||
.OpaqueType => |ident| retWithName("opaque", ident),
|
.OpaqueType => |ident| retWithName("opaque", ident),
|
||||||
.Struct => |ident| retWithName("struct", ident),
|
.Struct => |ident| retWithName("struct", ident),
|
||||||
|
|
|
@ -2,7 +2,7 @@ const std = @import("std");
|
||||||
const ast = @import("ast.zig");
|
const ast = @import("ast.zig");
|
||||||
const llvm = @import("llvm.zig");
|
const llvm = @import("llvm.zig");
|
||||||
const comp = @import("comp_ctx.zig");
|
const comp = @import("comp_ctx.zig");
|
||||||
// const analysis = @import("analysis.zig");
|
const types = @import("types.zig");
|
||||||
|
|
||||||
fn sliceify(non_slice: ?[*]const u8) []const u8 {
|
fn sliceify(non_slice: ?[*]const u8) []const u8 {
|
||||||
if (non_slice == null) return "";
|
if (non_slice == null) return "";
|
||||||
|
@ -85,17 +85,10 @@ pub const Codegen = struct {
|
||||||
.Literal => |literal| blk: {
|
.Literal => |literal| blk: {
|
||||||
break :blk switch (literal) {
|
break :blk switch (literal) {
|
||||||
// TODO other literals
|
// TODO other literals
|
||||||
.Integer32 => |val| llvm.LLVMConstInt(
|
.Integer => |val| blk2: {
|
||||||
llvm.LLVMInt32Type(),
|
var val_cstr = try std.cstr.addNullByte(self.allocator, val);
|
||||||
@intCast(c_ulonglong, val),
|
break :blk2 llvm.LLVMConstIntOfString(llvm.LLVMInt32Type(), val_cstr.ptr, 10);
|
||||||
10,
|
},
|
||||||
),
|
|
||||||
.Integer64 => |val| llvm.LLVMConstInt(
|
|
||||||
llvm.LLVMInt64Type(),
|
|
||||||
@intCast(c_ulonglong, val),
|
|
||||||
10,
|
|
||||||
),
|
|
||||||
|
|
||||||
.Float => |val| blk2: {
|
.Float => |val| blk2: {
|
||||||
var val_cstr = try std.cstr.addNullByte(self.allocator, val);
|
var val_cstr = try std.cstr.addNullByte(self.allocator, val);
|
||||||
break :blk2 llvm.LLVMConstRealOfString(llvm.LLVMDoubleType(), val_cstr.ptr);
|
break :blk2 llvm.LLVMConstRealOfString(llvm.LLVMDoubleType(), val_cstr.ptr);
|
||||||
|
|
|
@ -9,7 +9,6 @@ pub const TypeList = std.ArrayList(SymbolUnderlyingType);
|
||||||
pub const SymbolUnderlyingTypeEnum = enum {
|
pub const SymbolUnderlyingTypeEnum = enum {
|
||||||
Integer32,
|
Integer32,
|
||||||
Integer64,
|
Integer64,
|
||||||
Double,
|
|
||||||
Bool,
|
Bool,
|
||||||
|
|
||||||
// opaque unsolved identifier
|
// opaque unsolved identifier
|
||||||
|
@ -23,7 +22,6 @@ pub const SymbolUnderlyingTypeEnum = enum {
|
||||||
pub const SymbolUnderlyingType = union(SymbolUnderlyingTypeEnum) {
|
pub const SymbolUnderlyingType = union(SymbolUnderlyingTypeEnum) {
|
||||||
Integer32: void,
|
Integer32: void,
|
||||||
Integer64: void,
|
Integer64: void,
|
||||||
Double: void,
|
|
||||||
Bool: void,
|
Bool: void,
|
||||||
OpaqueType: []const u8,
|
OpaqueType: []const u8,
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ const scanners = @import("scanners.zig");
|
||||||
const parsers = @import("parsers.zig");
|
const parsers = @import("parsers.zig");
|
||||||
const printer = @import("ast_printer.zig");
|
const printer = @import("ast_printer.zig");
|
||||||
const codegen = @import("codegen.zig");
|
const codegen = @import("codegen.zig");
|
||||||
const analysis = @import("analysis.zig");
|
const types = @import("types.zig");
|
||||||
|
|
||||||
pub const Result = enum {
|
pub const Result = enum {
|
||||||
Ok,
|
Ok,
|
||||||
|
@ -50,7 +50,7 @@ pub fn run(allocator: *std.mem.Allocator, slice: []const u8) !Result {
|
||||||
std.debug.warn("parse tree\n");
|
std.debug.warn("parse tree\n");
|
||||||
printer.printNode(root, 0);
|
printer.printNode(root, 0);
|
||||||
|
|
||||||
var solver = try analysis.TypeSolver.init(allocator);
|
var solver = types.TypeSolver.init(allocator);
|
||||||
var ctx = try solver.pass(root);
|
var ctx = try solver.pass(root);
|
||||||
|
|
||||||
std.debug.warn("symbol table\n");
|
std.debug.warn("symbol table\n");
|
||||||
|
|
|
@ -14,7 +14,6 @@ const TokenType = tokens.TokenType;
|
||||||
pub const ParseError = error{
|
pub const ParseError = error{
|
||||||
CompileError,
|
CompileError,
|
||||||
UnknownOperator,
|
UnknownOperator,
|
||||||
InvalidInteger,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Node = ast.Node;
|
const Node = ast.Node;
|
||||||
|
@ -357,22 +356,11 @@ pub const Parser = struct {
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mkInt32(self: *Parser, val: i32) !*ast.Expr {
|
fn mkInteger(self: *Parser, val: []const u8) !*ast.Expr {
|
||||||
var expr = try self.allocator.create(Expr);
|
var expr = try self.allocator.create(Expr);
|
||||||
expr.* = Expr{
|
expr.* = Expr{
|
||||||
.Literal = ast.LiteralExpr{
|
.Literal = ast.LiteralExpr{
|
||||||
.Integer32 = val,
|
.Integer = val,
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
return expr;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mkInt64(self: *Parser, val: i64) !*ast.Expr {
|
|
||||||
var expr = try self.allocator.create(Expr);
|
|
||||||
expr.* = Expr{
|
|
||||||
.Literal = ast.LiteralExpr{
|
|
||||||
.Integer64 = val,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1136,40 +1124,8 @@ pub const Parser = struct {
|
||||||
.False => try self.mkBool(false),
|
.False => try self.mkBool(false),
|
||||||
.True => try self.mkBool(true),
|
.True => try self.mkBool(true),
|
||||||
|
|
||||||
.Integer => blk: {
|
.Integer => try self.mkInteger(lexeme),
|
||||||
// hacky, but it works. the old approach was doing
|
|
||||||
// parseInt(i64) on the catch block of parseInt(i32)
|
|
||||||
var i32_num_opt: ?i32 = std.fmt.parseInt(i32, lexeme, 10) catch null;
|
|
||||||
var i64_num: i64 = std.fmt.parseInt(i64, lexeme, 10) catch |err| {
|
|
||||||
return self.doError(
|
|
||||||
"Invalid integer (not 32bit or 64bit) '{}': {}",
|
|
||||||
lexeme,
|
|
||||||
err,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (i32_num_opt) |i32_num| {
|
|
||||||
break :blk try self.mkInt32(i32_num);
|
|
||||||
} else {
|
|
||||||
break :blk try self.mkInt64(i64_num);
|
|
||||||
}
|
|
||||||
|
|
||||||
// old one
|
|
||||||
|
|
||||||
//break :blk try self.mkInt32(std.fmt.parseInt(i32, lexeme, 10) catch |err32| {
|
|
||||||
// break :blk try self.mkInt64(std.fmt.parseInt(i64, lexeme, 10) catch |err64| {
|
|
||||||
// return self.doError(
|
|
||||||
// "Invalid integer (not 32bit or 64bit) '{}': {}",
|
|
||||||
// lexeme,
|
|
||||||
// err64,
|
|
||||||
// );
|
|
||||||
// });
|
|
||||||
//});
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO std.fmt.parseFloat
|
|
||||||
.Float => try self.mkFloat(lexeme),
|
.Float => try self.mkFloat(lexeme),
|
||||||
|
|
||||||
.String => try self.mkString(lexeme),
|
.String => try self.mkString(lexeme),
|
||||||
.Identifier => try self.mkVariable(self.peek()),
|
.Identifier => try self.mkVariable(self.peek()),
|
||||||
|
|
||||||
|
|
|
@ -16,13 +16,8 @@ pub const TypeSolver = struct {
|
||||||
err_tok: ?Token = null,
|
err_tok: ?Token = null,
|
||||||
hadError: bool = false,
|
hadError: bool = false,
|
||||||
|
|
||||||
err_ctx_buffer: []u8,
|
pub fn init(allocator: *std.mem.Allocator) TypeSolver {
|
||||||
|
return TypeSolver{ .allocator = allocator };
|
||||||
pub fn init(allocator: *std.mem.Allocator) !TypeSolver {
|
|
||||||
return TypeSolver{
|
|
||||||
.allocator = allocator,
|
|
||||||
.err_ctx_buffer = try allocator.alloc(u8, 512),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setErrContext(self: *@This(), comptime fmt: ?[]const u8, args: ...) void {
|
fn setErrContext(self: *@This(), comptime fmt: ?[]const u8, args: ...) void {
|
||||||
|
@ -31,11 +26,9 @@ pub const TypeSolver = struct {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.err_ctx = std.fmt.bufPrint(
|
// TODO allocate buffer on init() and use it
|
||||||
self.err_ctx_buffer,
|
var buf = self.allocator.alloc(u8, 256) catch unreachable;
|
||||||
fmt.?,
|
self.err_ctx = std.fmt.bufPrint(buf, fmt.?, args) catch unreachable;
|
||||||
args,
|
|
||||||
) catch unreachable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setErrToken(self: *@This(), tok: ?Token) void {
|
fn setErrToken(self: *@This(), tok: ?Token) void {
|
||||||
|
@ -45,7 +38,7 @@ pub const TypeSolver = struct {
|
||||||
fn doError(self: *@This(), comptime fmt: []const u8, args: ...) void {
|
fn doError(self: *@This(), comptime fmt: []const u8, args: ...) void {
|
||||||
self.hadError = true;
|
self.hadError = true;
|
||||||
|
|
||||||
std.debug.warn("analysis error");
|
std.debug.warn("type error");
|
||||||
if (self.err_tok) |tok| {
|
if (self.err_tok) |tok| {
|
||||||
std.debug.warn(" at line {}", tok.line);
|
std.debug.warn(" at line {}", tok.line);
|
||||||
}
|
}
|
||||||
|
@ -60,41 +53,35 @@ pub const TypeSolver = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve a type in global scope
|
/// Resolve a type in global scope
|
||||||
/// Properly resolves composite (currently opaque) types to structs/enums.
|
|
||||||
fn resolveGlobalType(
|
fn resolveGlobalType(
|
||||||
self: *@This(),
|
self: *@This(),
|
||||||
ctx: *comp.CompilationContext,
|
ctx: *comp.CompilationContext,
|
||||||
identifier: []const u8,
|
identifier: []const u8,
|
||||||
) ?SymbolUnderlyingType {
|
) ?SymbolUnderlyingType {
|
||||||
// first, we assume the identifier is for a simple type
|
// assume the identifier references a builtin
|
||||||
// if we fail (and this always returns OpaqueType as a fallback),
|
|
||||||
// we take it and find something in global scope
|
|
||||||
var typ = ctx.solveType(identifier);
|
var typ = ctx.solveType(identifier);
|
||||||
|
|
||||||
switch (typ) {
|
switch (typ) {
|
||||||
.OpaqueType => |val| {
|
.OpaqueType => |val| {
|
||||||
|
// solve for opaque so it isnt opaque
|
||||||
var sym = ctx.symbol_table.get(val);
|
var sym = ctx.symbol_table.get(val);
|
||||||
|
if (sym != null)
|
||||||
if (sym == null) {
|
|
||||||
self.doError("Unknown type: '{}'", val);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return switch (sym.?.value) {
|
return switch (sym.?.value) {
|
||||||
.Struct => SymbolUnderlyingType{ .Struct = val },
|
.Struct => SymbolUnderlyingType{ .Struct = val },
|
||||||
.Enum => SymbolUnderlyingType{ .Enum = val },
|
.Enum => SymbolUnderlyingType{ .Enum = val },
|
||||||
|
|
||||||
// TODO name resolution
|
|
||||||
|
|
||||||
else => blk: {
|
else => blk: {
|
||||||
self.doError(
|
self.doError(
|
||||||
"expected struct or enum for '{}', got {}",
|
"expected struct or enum for type '{}', got {}",
|
||||||
val,
|
val,
|
||||||
@tagName(comp.SymbolType(sym.?.value)),
|
sym,
|
||||||
);
|
);
|
||||||
break :blk null;
|
break :blk null;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.doError("Unknown type: '{}'", val);
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
else => return typ,
|
else => return typ,
|
||||||
|
@ -115,7 +102,6 @@ pub const TypeSolver = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compare if the given type names are equal.
|
|
||||||
fn compositeIdentifierEqual(
|
fn compositeIdentifierEqual(
|
||||||
self: *@This(),
|
self: *@This(),
|
||||||
typ_enum: comp.SymbolUnderlyingTypeEnum,
|
typ_enum: comp.SymbolUnderlyingTypeEnum,
|
||||||
|
@ -209,9 +195,7 @@ pub const TypeSolver = struct {
|
||||||
.Bool => SymbolUnderlyingType{ .Bool = {} },
|
.Bool => SymbolUnderlyingType{ .Bool = {} },
|
||||||
|
|
||||||
// TODO determine its i64 depending of parseInt results
|
// TODO determine its i64 depending of parseInt results
|
||||||
.Integer32 => SymbolUnderlyingType{ .Integer32 = {} },
|
.Integer => SymbolUnderlyingType{ .Integer32 = {} },
|
||||||
.Integer64 => SymbolUnderlyingType{ .Integer64 = {} },
|
|
||||||
.Float => SymbolUnderlyingType{ .Double = {} },
|
|
||||||
|
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
Loading…
Add table
Add a link
Reference in a new issue