Compare commits

..

No commits in common. "a081de93ce2bfbe621f82b4418f949522a71101d" and "2cb328c3f9bb17193f6805cf641053cccc71cd17" have entirely different histories.

8 changed files with 38 additions and 112 deletions

View file

@ -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 {

View file

@ -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,

View file

@ -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),

View file

@ -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);

View file

@ -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,

View file

@ -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");

View file

@ -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()),

View file

@ -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,
}; };