authorgravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-05-25 22:45:31-07:00
committergravatar for me@paperdave.netdave caruso <me@paperdave.net> 2024-05-25 22:45:31-07:00
log74ce0c381324aeb3b73bb9928bcf433ee7e0e687
tree50c429271ba632c4d9a6a3bda649ed95e5b9c147
parent84de5f3373a98b314e681e6e7d5ac259071c50c4
signature Commit is signed but in an unrecognized format.

implement some more checks


13 files changed, 347 insertions(+), 371 deletions(-)

CHANGELOG.md+9-2
......@@ -1,7 +1,14 @@
11## 0.1.2
22
3- feat: semantic analysis can return formatted errors to the user
4- feat: implement basic type coersions
5- feat: type-check call parameters
6- feat: type-check function return types
37- feat: parsing of assignments within expressions is disallowed now
4- feat: debug builds have `--report-trace` to print stack traces
8- feat: limit parsing to 4096 nested expressions / modules
9- feat: added error for return outside of function
10- feat: debug builds have `--report-trace` to print a stack trace per error
11- fix: function arguments were not validated
512- fix: unused warnings now properly work
613- fix: empty files and functions no longer crash
714
......@@ -16,4 +23,4 @@
1623
1724## 0.1.0
1825
19initial demo of compiler pipeline in zig.
26initial demo of compiler pipeline
README.md+5-2
......@@ -1,4 +1,4 @@
1# Compiler
1# New Language Project
22
33This language is a mix of features from JavaScript, Rust, and Zig. Some notable features carried over from each include:
44
......@@ -8,7 +8,7 @@ This language is a mix of features from JavaScript, Rust, and Zig. Some notable
88
99## Standard Library
1010
11Currently, there is no standard library. One will be added later.
11Currently, there is no standard library. One will be introduced later.
1212
1313## Hello World
1414
......@@ -77,3 +77,6 @@ var number = 0; // `number` is a `float`, but not explicit
7777var number: int = 0; // `number` is `int`
7878var number: float = 0; // `number` is `float`
7979```
80
81## Functions
82
demo.ix deleted-1
......@@ -1 +0,0 @@
1let x: int = 1.5;
notes deleted-19
......@@ -1,19 +0,0 @@
10.1.2
2- teg: useless discard
3- teg: dangling identifier
4- teg: remove `let _ = ` error
5- sema: .as instruction
6- sema: binary op type resolution (float + num_literal -> float)
70.1.3
8- parse/teg/sema: implement the comptime keyword
9- implement all the binary operators
10- implement all the unary operators
11- implement string escapes
12- implement proper float printing
13- js/link: add parenthesis when needed
140.1.4
15- parse: impl module, struct define, struct init, defer, and optionals
16- teg: impl the above, except anonymous struct initializers
17- sema: implement some built-in functions on floats
18 - float.intTruncate() to perform lossy int conversion
19
src/compiler/Air.Dump.zig+1-1
......@@ -235,7 +235,7 @@ pub const State = struct {
235235 .func => |func| {
236236 try tty.setColor(w, .bright_black);
237237 try w.writeAll("<");
238 try s.dumpValue(w, func.ty);
238 try s.dumpValue(w, func.ty.value());
239239 try tty.setColor(w, .bright_black);
240240 try w.writeAll(", ");
241241 const decl_name = s.ip.declPtrConst(func.owner_decl).name.toSlice(s.ip);
src/compiler/Ast.zig-85
......@@ -520,91 +520,6 @@ pub fn nodeToken(ast: Ast, node: NodeIndex) TokenIndex {
520520 return ast.nodes.items(.main_token)[node];
521521}
522522
523// pub fn operandNode(ast: Ast, node: NodeIndex, operand: OperandId) NodeIndex {
524// if (operand == .main_node) return node;
525
526// switch (ast.nodeTag(node)) {
527// .root => unreachable,
528// .block_void => unreachable,
529// .block_implicit_return => unreachable,
530// .var_decl,
531// => return switch (operand) {
532// .type => ast.nodeData(node).var_decl.type.unwrap().?,
533// .initializer => ast.nodeData(node).var_decl.init,
534// else => unreachable,
535// },
536
537// .label,
538// .group,
539// .negate,
540// .bit_not,
541// .bool_not,
542// .@"comptime",
543// => return switch (operand) {
544// .base => ast.nodeData(node).un.expr,
545// else => unreachable, // Invalid unary operand
546// },
547
548// .add,
549// .sub,
550// .mul,
551// .div,
552// .rem,
553// .pow,
554// .@"and",
555// .@"or",
556// .bit_and,
557// .bit_or,
558// .bit_xor,
559// .shl,
560// .shr,
561// .eq,
562// .ne,
563// .lt,
564// .le,
565// .gt,
566// .ge,
567// .assign,
568// .add_assign,
569// .sub_assign,
570// .mul_assign,
571// .div_assign,
572// .rem_assign,
573// .pow_assign,
574// .and_assign,
575// .or_assign,
576// .bit_and_assign,
577// .bit_or_assign,
578// .bit_xor_assign,
579// .shl_assign,
580// .shr_assign,
581// => {
582// const bin = ast.nodeData(node).bin;
583// return switch (operand) {
584// .lhs => bin.lhs,
585// .rhs => bin.rhs,
586// else => unreachable, // Not a valid operand for binary node
587// };
588// },
589
590// .fn_decl,
591// .param_decl,
592// .id,
593// .number_int,
594// .number_float,
595// .string,
596// .multiline_string,
597// .call,
598// .builtin_call,
599// .@"if",
600// .field,
601// .index,
602// .@"return",
603// .@"break",
604// => |tag| std.debug.panic("TODO: operandNode .{s}", .{@tagName(tag)}),
605// }
606// }
607
608523pub fn fullFnDecl(ast: Ast, node: NodeIndex) full.FnDecl {
609524 const tok_tags = ast.tokens.items(.tag);
610525
src/compiler/Comp.zig+2-2
......@@ -122,7 +122,7 @@ pub const File = struct {
122122 return loaded_source;
123123 }
124124
125 const GetAstError = GetSourceError || Parse.Error;
125 pub const GetAstError = GetSourceError || Parse.Error;
126126
127127 pub fn getAst(file: *File, comp: *Comp) GetAstError!Ast {
128128 if (file.ast) |ast| {
......@@ -155,7 +155,7 @@ pub const File = struct {
155155 return ast;
156156 }
157157
158 const GetTegError = GetAstError || Transform.Error;
158 pub const GetTegError = GetAstError || Transform.Error;
159159
160160 pub fn getTeg(file: *File, comp: *Comp, fail_report: *Report) GetTegError!Teg {
161161 if (file.teg) |teg| {
src/compiler/InternPool.zig+66-8
......@@ -137,7 +137,7 @@ pub const Tag = enum(u8) {
137137 pub const FuncDecl = struct {
138138 analysis: FuncAnalysis,
139139 owner_decl: DeclIndex,
140 ty: Value,
140 ty: Type,
141141 teg_body_inst: TrackedInst.Index,
142142 };
143143
......@@ -252,8 +252,7 @@ pub const Value = enum(u32) {
252252 .int32 => .int32,
253253 .float => .float,
254254 .string => .string,
255
256 .func => @panic("TODO"),
255 .func => value.toKey(ip).func.ty,
257256 };
258257 }
259258
......@@ -274,6 +273,13 @@ pub const Value = enum(u32) {
274273 };
275274 }
276275
276 pub fn toTypeUnsafe(value: Value, proof_ip: *const InternPool) Type {
277 if (std.debug.runtime_safety) {
278 return value.toType(proof_ip).?;
279 }
280 return @enumFromInt(@intFromEnum(value));
281 }
282
277283 pub fn asString(val: Value, ip: *const InternPool) InternPool.String.WithLen {
278284 return val.valueData(ip, .string).get(ip);
279285 }
......@@ -298,6 +304,12 @@ pub const Type = enum(u32) {
298304 void = @intFromEnum(Value.void),
299305 _,
300306
307 // Restricted version of `InternPool.Tag`
308 pub const Tag = enum(u32) {
309 type_function = @intFromEnum(InternPool.Tag.type_function),
310 simple_type = @intFromEnum(InternPool.Tag.simple_type),
311 };
312
301313 /// An array of `Index` existing within the `extra` array.
302314 /// This type exists to provide a struct with lifetime that is
303315 /// not invalidated when items are added to the `InternPool`.
......@@ -314,9 +326,59 @@ pub const Type = enum(u32) {
314326 return @enumFromInt(@intFromEnum(ty));
315327 }
316328
329 pub fn tag(ty: Type, ip: *const InternPool) Type.Tag {
330 return @enumFromInt(@intFromEnum(ty.value().valueTag(ip)));
331 }
332
333 pub fn toFunctionPrototype(ty: Type, ip: *const InternPool) ?Key.TypeFunction {
334 if (ty.tag(ip) != .type_function) return null;
335 return ip.indexToKey(ty.value()).type_function;
336 }
337
338 pub fn displayName(ty: Type, ip: *const InternPool) std.fmt.Formatter(formatDisplayName) {
339 return .{ .data = .{ .type = ty, .ip = ip } };
340 }
341
317342 pub fn dump(ty: Type, ip: *const InternPool) std.fmt.Formatter(Value.formatValue) {
318343 return ty.value().dump(ip);
319344 }
345
346 fn formatDisplayName(
347 ctx: struct { type: Type, ip: *const InternPool },
348 comptime _: []const u8,
349 _: std.fmt.FormatOptions,
350 writer: anytype,
351 ) !void {
352 const ty = ctx.type;
353 const ip = ctx.ip;
354
355 switch (ty) {
356 .@"opaque" => return try writer.writeAll("opaque"),
357 .bool => return try writer.writeAll("bool"),
358 .int32 => return try writer.writeAll("int"),
359 .float => return try writer.writeAll("float"),
360 .string => return try writer.writeAll("string"),
361 .noreturn => return try writer.writeAll("noreturn"),
362 .type => return try writer.writeAll("type"),
363 .numeric_literal => return try writer.writeAll("numeric literal"),
364 .void => return try writer.writeAll("void"),
365 else => {},
366 }
367
368 switch (ty.tag(ip)) {
369 .simple_type => unreachable, // all simple types are already printed
370 .type_function => {
371 const type_function = ty.toFunctionPrototype(ip).?;
372 try writer.writeAll("fn(");
373 for (type_function.param_types, 0..) |param, i| {
374 if (i != 0) try writer.writeAll(", ");
375 try writer.print("{}", .{param.displayName(ip)});
376 }
377 try writer.writeAll(") -> ");
378 try writer.print("{}", .{type_function.return_type.displayName(ip)});
379 },
380 }
381 }
320382};
321383
322384pub const static_keys = [_]Key{
......@@ -386,10 +448,6 @@ pub const String = enum(u32) {
386448 pub fn tryToNullTerminatedString(string: WithLen, ip: *InternPool, gpa: Allocator) !NullTerminatedString {
387449 return string.string.tryToNullTerminatedString(string.len, ip, gpa);
388450 }
389
390 pub fn toNullTerminatedString(string: WithLen, ip: *const InternPool) NullTerminatedString {
391 return string.string.tryToNullTerminatedString(string.len, ip);
392 }
393451 };
394452
395453 pub fn toSlice(string: String, len: u64, ip: *const InternPool) []const u8 {
......@@ -678,7 +736,7 @@ pub const Key = union(enum) {
678736 };
679737
680738 pub const Func = struct {
681 ty: Value,
739 ty: Type,
682740 owner_decl: DeclIndex,
683741 teg_body_inst: TrackedInst.Index,
684742
src/compiler/Parse.zig+40-1
......@@ -12,6 +12,11 @@ scratch: ArrayListUnmanaged(NodeIndex) = .{},
1212
1313ast_error: ?AstError.WithTrace = null,
1414
15depth: u32 = 0,
16
17/// Instead of implementing a non-recursive parser, the stack size is limited by this constant.
18const depth_limit = 4096;
19
1520pub fn parse(gpa: std.mem.Allocator, source: [:0]const u8, ast_error: *AstError.WithTrace) Parse.Error!Ast {
1621 var err_byte: ByteOffset = 0;
1722 var tokens = Tokenizer.tokenize(
......@@ -129,6 +134,8 @@ pub const AstError = union(enum) {
129134 expected: ExpectedContext,
130135 got: Token,
131136 },
137 /// Went past depth maximum
138 depth_limit: Token,
132139
133140 pub fn toReport(err: AstError, comp: *Comp, file: *Comp.File, trace: Report.DebugStoredTrace) !Report {
134141 return (Report.Err{ .ast = err }).toReport(comp, file, trace);
......@@ -165,6 +172,9 @@ pub const AstError = union(enum) {
165172 data.got.tag.description(),
166173 });
167174 },
175 .depth_limit => |data| {
176 try ctx.msg(.{ .token_stored = data }, "syntax tree is nested too deeply (limit: {d})", .{depth_limit});
177 },
168178 }
169179 }
170180
......@@ -200,6 +210,12 @@ pub fn parseRoot(p: *Parse) Error!void {
200210}
201211
202212fn parseModuleMembers(p: *Parse) Error!SubList {
213 p.depth += 1;
214 defer p.depth -= 1;
215 if (p.depth > depth_limit) {
216 return p.fail(.{ .depth_limit = p.fullToken(p.pos) });
217 }
218
203219 const trace = t.trace(@src());
204220 defer trace.end();
205221
......@@ -642,7 +658,24 @@ const Operator = enum(u8) {
642658 }
643659
644660 pub fn isAssign(op: Operator) bool {
645 return @intFromEnum(op) >= @intFromEnum(Operator.assign);
661 return switch (op) {
662 .assign,
663 .add_assign,
664 .sub_assign,
665 .mul_assign,
666 .div_assign,
667 .rem_assign,
668 .pow_assign,
669 .and_assign,
670 .or_assign,
671 .bit_and_assign,
672 .bit_or_assign,
673 .bit_xor_assign,
674 .shl_assign,
675 .shr_assign,
676 => true,
677 else => false,
678 };
646679 }
647680};
648681
......@@ -650,6 +683,12 @@ fn parseExpression(p: *Parse, allow_assign_operators: bool) Error!NodeIndex {
650683 const trace = t.trace(@src());
651684 defer trace.end();
652685
686 p.depth += 1;
687 defer p.depth -= 1;
688 if (p.depth > depth_limit) {
689 return p.fail(.{ .depth_limit = p.fullToken(p.pos) });
690 }
691
653692 assert(p.isExpressionStart());
654693
655694 // These unary operators are greedy, so handle them first.
src/compiler/Report.zig+78-20
......@@ -64,16 +64,6 @@ pub const Err = union(enum) {
6464 );
6565 }
6666 },
67 invalid_discard: struct {
68 node: NodeIndex,
69
70 fn build(data: @This(), ctx: *MsgContext) !void {
71 try ctx.msgToken(ctx.ast.idToken(data.node), "local cannot be named \"_\"", .{});
72 try ctx.msgAlone("to discard this value, remove '{s}'", .{
73 ctx.ast.tokenSlice(ctx.ast.nodeToken(data.node)),
74 });
75 }
76 },
7767 duplicate_function: struct {
7868 node: NodeIndex,
7969 prev: NodeIndex,
......@@ -106,7 +96,7 @@ pub const Err = union(enum) {
10696 fn build(data: @This(), ctx: *MsgContext) !void {
10797 try ctx.msgNode(
10898 data.node,
109 "return cannot be used here",
99 "return cannot be used outside of a function body",
110100 .{},
111101 );
112102 }
......@@ -149,7 +139,34 @@ pub const Err = union(enum) {
149139
150140 fn build(data: @This(), ctx: *MsgContext) !void {
151141 const node = try data.src.get(ctx);
152 try ctx.msgNode(node, "stinky", .{});
142 try ctx.msgNode(node, "expected \"{s}\", got \"{s}\"", .{
143 data.to.displayName(&ctx.comp.intern_pool),
144 data.from.displayName(&ctx.comp.intern_pool),
145 });
146 }
147 },
148 call_non_function: struct {
149 call: LazyNode,
150 ty: Type,
151
152 fn build(data: @This(), ctx: *MsgContext) !void {
153 const node = try data.call.get(ctx);
154 try ctx.msgNode(node, "\"{s}\" is not callable", .{
155 data.ty.displayName(&ctx.comp.intern_pool),
156 });
157 }
158 },
159 call_mismatch_args: struct {
160 call: LazyNode,
161 actual: u32,
162 expected: u32,
163
164 fn build(data: @This(), ctx: *MsgContext) !void {
165 const node = try data.call.get(ctx);
166 try ctx.msgNode(node, "function expected {d} arguments, recieved {d}", .{
167 data.expected,
168 data.actual,
169 });
153170 }
154171 },
155172};
......@@ -184,6 +201,20 @@ pub const Warn = union(enum) {
184201 try ctx.msgAlone("if this was intentional, discard it with \"_ = {s};\"", .{id});
185202 }
186203 },
204 invalid_discard: struct {
205 node: NodeIndex,
206
207 fn build(data: @This(), ctx: *MsgContext) !void {
208 try ctx.msgToken(ctx.ast.nodeToken(data.node), "\"let\" is not required for a discard", .{});
209 }
210 },
211 dangling_identifier: struct {
212 node: NodeIndex,
213
214 fn build(data: @This(), ctx: *MsgContext) !void {
215 try ctx.msgToken(ctx.ast.nodeToken(data.node), "dangling identifier does nothing", .{});
216 }
217 },
187218 numeric_int_var_ambiguity: struct {
188219 node: LazyNode,
189220
......@@ -243,7 +274,7 @@ pub const DebugStoredTrace = if (builtin.mode == .Debug)
243274 StoredTrace
244275else
245276 struct {
246 pub fn capture() @This() {
277 pub fn capture(_: ?usize) @This() {
247278 return .{};
248279 }
249280
......@@ -289,7 +320,7 @@ pub fn deinit(report: Report, gpa: Allocator) void {
289320}
290321
291322pub fn dumpToWriter(report: Report, comp: *Comp, writer: anytype, tty: std.io.tty.Config) !void {
292 try writer.print("{d} errors, {d} warnings\n", .{ report.errors.len, report.warnings.len });
323 // try writer.print("{d} errors, {d} warnings\n", .{ report.errors.len, report.warnings.len });
293324 for (report.errors) |item| {
294325 try report.formatItem(item, .@"error", comp, writer, tty);
295326 }
......@@ -653,14 +684,16 @@ pub const LazyNode = struct {
653684
654685 const node_offset = teg.instNodeOffset(lazy.inst);
655686
656 const base_node: NodeIndex = @intCast(node_offset - teg_decl.src_node);
687 const base_node: NodeIndex = @intCast(teg_decl.src_node + node_offset);
657688 if (lazy.operand == .main_node)
658689 return base_node;
659690
660691 const data = ctx.ast.nodeData(base_node);
661692
693 const node_tag = ctx.ast.nodeTag(base_node);
694
662695 switch (teg.instTag(lazy.inst)) {
663 .as => switch (ctx.ast.nodeTag(base_node)) {
696 .as => switch (node_tag) {
664697 .var_decl => {
665698 switch (lazy.operand) {
666699 .rhs => return data.var_decl.init,
......@@ -669,6 +702,23 @@ pub const LazyNode = struct {
669702 },
670703 else => {},
671704 },
705 .call => switch (node_tag) {
706 .call => switch (lazy.operand) {
707 .operand => return data.call.base,
708 else => |op| return ctx.ast.extraData(data.call.extra, Ast.Call)
709 .args.slice(ctx.ast)[op.getArgId()],
710 },
711 else => {},
712 },
713 .@"break" => switch (node_tag) {
714 .@"return" => return data.@"return".expr.unwrap().?,
715 .@"break" => return data.@"break".value.unwrap().?,
716 .block_implicit_return => {
717 const statements = ctx.ast.nodeStatements(base_node);
718 return statements[statements.len - 1];
719 },
720 else => return base_node,
721 },
672722 else => {},
673723 }
674724
......@@ -687,14 +737,22 @@ pub const Operand = enum(u32) {
687737 main_node = 0,
688738 /// Base of a call, field, or operand to a unary instruction
689739 operand = 1,
740 /// Return type of a function declaration or lambda node
741 return_type = 2,
690742 /// Left hand side of a binary instruction
691 lhs,
743 lhs = 4,
692744 /// Right hand side of a binary instruction
693 rhs,
745 rhs = 5,
694746
695 /// An argument of a call node
747 /// One of:
748 /// - An argument of a .call node
749 /// - A parameter of a .fn_decl node
696750 pub fn argument(id: u32) Operand {
697 return @enumFromInt(id + 2);
751 return @enumFromInt(id + 5);
752 }
753
754 fn getArgId(op: Operand) u32 {
755 return @intFromEnum(op) - 5;
698756 }
699757};
700758
src/compiler/Sema.zig+120-111
......@@ -23,14 +23,20 @@ report: Report.Builder = undefined,
2323air_instructions: std.MultiArrayList(Air.Inst) = .{},
2424air_extra: dod.ExtraStore(.air, .@"var") = .{},
2525
26temp_result_map: std.AutoArrayHashMapUnmanaged(Teg.Inst.Index, Type) = .{},
27
2628pub const Error = error{
2729 /// Analysis has failed, see error list
2830 AnalysisFail,
29 /// This analysis is waiting on other dependencies to finish
30 Interrupt,
31 // This analysis is waiting on other dependencies to finish
32 // Interrupt,
33
34 // TODO: add handling of this
35 TooManyValues,
36 HasNulls,
3137
3238 OutOfMemory,
33};
39} || Comp.File.GetTegError;
3440
3541/// Instead of using a hash map for this, just pre-allocate a list long enough to hold everything.
3642/// This is fine because all instructions are going to be adacent in a single declaration.
......@@ -104,6 +110,7 @@ pub fn deinit(s: *Sema) void {
104110 s.scratch.deinit(s.arena);
105111 s.air_extra.deinit(s.gpa);
106112 s.air_instructions.deinit(s.gpa);
113 s.temp_result_map.deinit(s.arena);
107114}
108115
109116pub fn finalize(s: *Sema) !void {
......@@ -121,7 +128,7 @@ pub fn finalize(s: *Sema) !void {
121128// --- analyze functions ---
122129
123130/// This is the entrypoint to semantic analysis: It all starts at a declaration.
124pub fn analyzeDeclaration(s: *Sema) anyerror!Value {
131pub fn analyzeDeclaration(s: *Sema) Error!Value {
125132 const t = tracer.trace(@src());
126133 defer t.end();
127134
......@@ -137,27 +144,43 @@ pub fn analyzeDeclaration(s: *Sema) anyerror!Value {
137144 defer s.gpa.free(s.params);
138145
139146 for (fn_decl.params.slice(&s.code), 0..) |param, i| {
140 s.params[i] = (try s.resolveRefComptime(param)).toType(s.ip).?;
147 s.params[i] = try s.resolveType(
148 s.inst_map.start.op(Report.Operand.argument(@intCast(i))),
149 param,
150 );
141151 }
142152
143 const return_type = (try s.resolveRefComptime(fn_decl.return_type)).toType(s.ip).?;
144
145 const root_block = fn_decl.body.toIndex() orelse
146 @panic("TODO: please change this to Index. Ref is not allowed here anymore");
153 const return_type = try s.resolveType(
154 s.inst_map.start.op(.return_type),
155 fn_decl.return_type,
156 );
147157
148 _ = if (s.code.instPayload(.block, root_block).len > 0)
149 try s.analyzeBlock(root_block)
150 else empty_block: {
151 var root = try s.addBlock();
152 break :empty_block (try root.finish(s, s.air_instructions.len - 1));
153 };
158 const root_block = fn_decl.body;
159 switch (root_block.decode()) {
160 .value => |value| {
161 const coerced = try s.coerceValue(
162 s.inst_map.start.op(.operand),
163 value,
164 return_type,
165 );
166 const block = try s.addBlock();
167 _ = try s.addInstRef(.@"break", .{
168 .block_ref = block.block_index,
169 .value = coerced.toAir(),
170 });
171 },
172 .index => |index| {
173 try s.temp_result_map.putNoClobber(s.arena, index, return_type);
174 _ = try s.analyzeBlock(index);
175 },
176 }
154177
155 const func_prototype = try s.ip.get(s.gpa, .{
178 const func_prototype = (try s.ip.get(s.gpa, .{
156179 .type_function = .{
157180 .param_types = s.params,
158181 .return_type = return_type,
159182 },
160 });
183 })).toTypeUnsafe(s.ip);
161184 const value = try s.ip.get(s.gpa, .{
162185 .func = .{
163186 .ty = func_prototype,
......@@ -169,7 +192,7 @@ pub fn analyzeDeclaration(s: *Sema) anyerror!Value {
169192 return value;
170193}
171194
172pub fn analyzeBlock(s: *Sema, in: Teg.Inst.Index) anyerror!Air.Inst.Ref {
195pub fn analyzeBlock(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
173196 const t = tracer.trace(@src());
174197 defer t.end();
175198
......@@ -224,7 +247,7 @@ fn analyzeCode(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
224247 return out;
225248}
226249
227fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
250fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) Error!Air.Inst.Ref {
228251 switch (s.code.instTag(in)) {
229252 .string => {
230253 const string: Teg.SlicedString = s.code.instPayload(.string, in);
......@@ -236,16 +259,14 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
236259 },
237260 .js => {
238261 const operand_ref = s.code.instData(.js, in).operand;
239 const operand = try s.resolveRefComptime(operand_ref);
240 // const operand_str = try s.coerceValue(operand_ref.toIndex().?, operand, .string);
262 const operand = try s.resolveRefComptimeType(in.op(.operand), operand_ref, .string);
241263 return s.addInstRef(.js_global_ref, operand.asString(s.ip));
242264 },
243265 .field => {
244266 const field: Teg.Inst.Bin = s.code.instPayload(.field, in);
245267 const base = try s.resolveRefRuntime(field.lhs);
246 const rhs = try s.resolveRefComptime(field.rhs);
247268
248 // try s.validateType(rhs.type(s.ip), .string_type);
269 const rhs = try s.resolveRefComptimeType(in.op(.rhs), field.rhs, .string);
249270 const string = rhs.asString(s.ip);
250271
251272 const base_type = s.typeOfRef(base);
......@@ -331,66 +352,71 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
331352 const brk: Teg.Inst.Break = s.code.instPayload(.@"break", in);
332353 const operand = try s.resolveRefRuntime(brk.operand);
333354 const block_ref = s.inst_map.get(brk.block).?.decode().index; // block ref is resolved by teg
355
356 const coerced = if (s.temp_result_map.get(brk.block)) |ty|
357 try s.coerceRef(in.op(.operand), operand, ty)
358 else
359 operand;
360
334361 return s.addInstRef(.@"break", .{
335362 .block_ref = block_ref,
336 .value = operand,
363 .value = coerced,
337364 });
338365 },
339366 .call => {
340 @panic("TODO: re-implement .call");
341 // const call: Teg.Inst.Call = s.code.instPayload(.call, in);
342 // const base = try s.resolveRefRuntime(call.base);
343 // const base_type = s.typeOfRef(base).toKey(s.ip);
344
345 // const is_opaque = base_type == .simple_type and base_type.simple_type == .@"opaque";
346
347 // const scratch = try s.arena.alloc(Air.Inst.Ref, call.args.len);
348 // defer s.arena.free(scratch);
349 // if (!is_opaque) {
350 // if (base_type != .type_function) {
351 // std.debug.print("Attempt to call non-function {}", .{base_type});
352 // return error.AnalysisFail;
353 // }
354
355 // const proto = base_type.type_function;
356 // if (proto.param_types.len != call.args.len) {
357 // std.debug.print("Incorrect args. Expect {d}, Got {d}", .{
358 // proto.param_types.len,
359 // call.args.len,
360 // });
361 // return error.AnalysisFail;
362 // }
363
364 // // validate function args
365 // for (proto.param_types, call.args.slice(&s.code), 0..) |param, arg, i| {
366 // const ref = try s.resolveRefRuntime(arg);
367 // scratch[i] = ref;
368 // _ = param;
369 // // try s.validateType(
370 // // Value.fromInterned(s.typeOfRef(ref)),
371 // // param,
372 // // );
373 // }
374 // } else {
375 // // just copy the args in without checking
376 // for (call.args.slice(&s.code), 0..) |arg, i| {
377 // const ref = try s.resolveRefRuntime(arg);
378 // scratch[i] = ref;
379 // }
380 // }
367 const call: Teg.Inst.Call = s.code.instPayload(.call, in);
368 const base = try s.resolveRefRuntime(call.base);
369 const base_type = s.typeOfRef(base);
381370
382 // const start = s.air_extra.list.items.len;
383 // try s.air_extra.list.appendSlice(s.gpa, @ptrCast(scratch));
371 const scratch = try s.arena.alloc(Air.Inst.Ref, call.args.len);
372 defer s.arena.free(scratch);
373 if (base_type != .@"opaque") {
374 const proto = base_type.toFunctionPrototype(s.ip) orelse {
375 return s.fail(.call_non_function, .{
376 .call = s.nodeLoc(in, .operand),
377 .ty = base_type,
378 });
379 };
380
381 if (proto.param_types.len != call.args.len) {
382 // TODO: also attempt to coerce some of the params to reveal more errors.
383 return s.fail(.call_mismatch_args, .{
384 .call = s.nodeLoc(in, .operand),
385 .actual = @intCast(call.args.len),
386 .expected = @intCast(proto.param_types.len),
387 });
388 }
389
390 // validate function args
391 for (proto.param_types, call.args.slice(&s.code), 0..) |param, arg, i| {
392 const ref = try s.resolveRefRuntime(arg);
393 const coerced = try s.coerceRef(
394 in.op(Report.Operand.argument(@intCast(i))),
395 ref,
396 param,
397 );
398 scratch[i] = coerced;
399 }
400 } else {
401 // just copy the args in without checking
402 for (call.args.slice(&s.code), 0..) |arg, i| {
403 const ref = try s.resolveRefRuntime(arg);
404 scratch[i] = ref;
405 }
406 }
384407
385 // return s.addInstRef(.call, .{
386 // .base = base,
387 // .call_data = try s.air_extra.append(s.gpa, Air.Call.Data, .{
388 // .args = .{
389 // .start = @intCast(start),
390 // .len = @intCast(scratch.len),
391 // },
392 // }),
393 // });
408 const start = s.air_extra.list.items.len;
409 try s.air_extra.list.appendSlice(s.gpa, @ptrCast(scratch));
410
411 return s.addInstRef(.call, .{
412 .base = base,
413 .call_data = try s.air_extra.append(s.gpa, Air.Call.Data, .{
414 .args = .{
415 .start = @intCast(start),
416 .len = @intCast(scratch.len),
417 },
418 }),
419 });
394420 },
395421 .ensure_void => {
396422 const operand = try s.resolveRefRuntimeExtra(s.code.instData(.ensure_void, in).operand, false);
......@@ -488,7 +514,7 @@ fn analyzeCodeInner(s: *Sema, in: Teg.Inst.Index) !Air.Inst.Ref {
488514 },
489515 .as => {
490516 const as: Teg.Inst.As = s.code.instPayload(.as, in);
491 const coerce_to = try s.resolveRefComptimeType(in.op(.lhs) ,as.type, .type);
517 const coerce_to = try s.resolveRefComptimeType(in.op(.lhs), as.type, .type);
492518 const operand = try s.resolveRefRuntime(as.operand);
493519 return try s.coerceRef(in.op(.rhs), operand, coerce_to.toType(s.ip).?);
494520 },
......@@ -501,16 +527,14 @@ fn markInst(s: *Sema, inst: Air.Inst.Index) void {
501527 flags.uses = flags.uses.increment();
502528}
503529
504fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) anyerror!Air.Inst.Ref {
530fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) Error!Air.Inst.Ref {
505531 const t = tracer.trace(@src());
506532 defer t.end();
507533
508534 const scratch_top = s.scratch.items.len;
509535 defer s.scratch.shrinkRetainingCapacity(scratch_top);
510536
511 std.debug.print("{d} {d}\n", .{ scratch_top, s.scratch.items.len });
512537 const visited = try s.resolveRefRuntime(in);
513 std.debug.print("{d} {d}\n", .{ scratch_top, s.scratch.items.len });
514538
515539 if (scratch_top == s.scratch.items.len and visited.kind == .value) {
516540 return visited;
......@@ -530,7 +554,7 @@ fn analyzeRefToBlock(s: *Sema, in: Teg.Inst.Ref) anyerror!Air.Inst.Ref {
530554 return (try block.finish(s, scratch_top)).toRef();
531555}
532556
533fn resolveRefRuntimeExtra(s: *Sema, ref: Teg.Inst.Ref, mark: bool) anyerror!Air.Inst.Ref {
557fn resolveRefRuntimeExtra(s: *Sema, ref: Teg.Inst.Ref, mark: bool) Error!Air.Inst.Ref {
534558 const index = ref.toIndex() orelse {
535559 return ref.toValue().?.toAir();
536560 };
......@@ -548,11 +572,11 @@ fn resolveRefRuntimeExtra(s: *Sema, ref: Teg.Inst.Ref, mark: bool) anyerror!Air.
548572 return s.analyzeCode(index);
549573}
550574
551fn resolveRefRuntime(s: *Sema, ref: Teg.Inst.Ref) anyerror!Air.Inst.Ref {
575fn resolveRefRuntime(s: *Sema, ref: Teg.Inst.Ref) Error!Air.Inst.Ref {
552576 return resolveRefRuntimeExtra(s, ref, true);
553577}
554578
555fn resolveRefComptime(s: *Sema, ref: Teg.Inst.Ref) !Value {
579fn resolveRefComptimeAny(s: *Sema, ref: Teg.Inst.Ref) !Value {
556580 const index = switch (ref.decode()) {
557581 .value => |value| return value,
558582 .index => |index| index,
......@@ -575,6 +599,15 @@ fn resolveRefComptime(s: *Sema, ref: Teg.Inst.Ref) !Value {
575599 }
576600}
577601
602fn resolveRefComptimeType(s: *Sema, src: Teg.InstOp, ref: Teg.Inst.Ref, ty: Type) !Value {
603 const any = try s.resolveRefComptimeAny(ref);
604 return try s.coerceValue(src, any, ty);
605}
606
607fn resolveType(s: *Sema, src: Teg.InstOp, ref: Teg.Inst.Ref) !Type {
608 return (try s.resolveRefComptimeType(src, ref, .type)).toTypeUnsafe(s.ip);
609}
610
578611/// Coerce `value` runtime ref to `coerce_type`, and emit an error at `teg_ref`'s location
579612fn coerceRef(s: *Sema, src: InstOp, value: Air.Inst.Ref, to: Type) !Air.Inst.Ref {
580613 const from = s.typeOfRef(value);
......@@ -628,30 +661,6 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value {
628661 return value;
629662 }
630663 switch (to) {
631 // .simple_type => |simple_type| switch (simple_type) {
632 // // .@"opaque",
633 // .int32 => {
634 // switch (value_type.toKey(s.ip)) {
635 // .simple_type => switch (simple_type) {
636 // // .int32 => return value,
637 // // .numeric_literal => {
638 // // const val = value.decode().value.toKey(s.ip).comptime_float;
639 // // },
640 // else => |tag| std.debug.panic("TODO: coerceTo value_type simple_type .{s}", .{@tagName(tag)}),
641 // },
642 // else => |tag| std.debug.panic("TODO: coerceTo value_type .{s}", .{@tagName(tag)}),
643 // }
644 // },
645 // // .float,
646 // // .string,
647 // // .bool,
648 // // .noreturn,
649 // // .type,
650 // // .numeric_literal,
651 // else => |tag| std.debug.panic("TODO: coerceTo coerce_type .simple_type = .{s}", .{@tagName(tag)}),
652 // },
653 // else => |tag| std.debug.panic("TODO: coerceTo coerce_type .{s}", .{@tagName(tag)}),
654 // }
655664 .@"opaque" => {
656665 //
657666 },
......@@ -691,18 +700,18 @@ fn coerceValue(s: *Sema, src: InstOp, value: Value, to: Type) !Value {
691700 //
692701 },
693702 .type => {
694 //
703 if (value == .void) return .void;
695704 },
696705 .numeric_literal => {},
697706 .void => {
698 //
707 if (value == .void) return .void;
699708 },
700709 else => {},
701710 }
702711 return s.coersionError(src, from, to);
703712}
704713
705fn resolveDecl(s: *Sema, decl_index: Decl.Index) !Value {
714fn resolveDecl(s: *Sema, decl_index: Decl.Index) Error!Value {
706715 const decl = s.ip.declPtr(decl_index);
707716 switch (decl.analysis) {
708717 .unreferenced => {
......@@ -945,7 +954,7 @@ fn coersionError(s: *Sema, src: InstOp, from: Type, to: Type) Error {
945954}
946955
947956fn lazyNode(s: *const Sema, src: InstOp) Report.LazyNode {
948 return src.lazyNode( s.decl_index);
957 return src.lazyNode(s.decl_index);
949958}
950959
951960const Sema = @This();
src/compiler/Transform.zig+22-8
......@@ -396,7 +396,7 @@ fn transformFunction(t: *Transform, outer_decl: BlockContext, node: NodeIndex) A
396396 });
397397
398398 t.getInstData(func).* = EncodedData.encode(.declaration, .{
399 .src_node = 0,
399 .src_node = outer_decl.nodeOffset(node),
400400 .extra_index = decl_extra,
401401 });
402402
......@@ -971,8 +971,8 @@ fn transformExpression(t: *Transform, decl: BlockContext, node: NodeIndex) Alloc
971971 const ret_node = t.ast.nodeData(node).@"return";
972972
973973 const ret_block_ptr = decl.return_target orelse {
974 // return error.InvalidReturn;
975 @panic("TODO");
974 try t.report.err(.invalid_return, .{ .node = node });
975 return .void;
976976 };
977977
978978 const ret_block = if (ret_block_ptr.*) |block|
......@@ -1061,9 +1061,18 @@ fn transformBlockStatement(
10611061 const var_decl = t.ast.fullVarDecl(node);
10621062
10631063 if (mem.eql(u8, var_decl.id, "_")) {
1064 // TODO: relax this restriction into just creating a discard node
1065 try t.report.err(.invalid_discard, .{ .node = node });
1066 return .void;
1064 try t.report.warn(.invalid_discard, .{ .node = node });
1065
1066 const ref = try t.transformExpression(decl, var_decl.init) orelse
1067 return null;
1068
1069 return try t.addInstRef(.{
1070 .tag = .discard,
1071 .data = EncodedData.encode(.discard, .{
1072 .src_node = decl.nodeOffset(node),
1073 .operand = ref,
1074 }),
1075 });
10671076 }
10681077
10691078 if (BuiltInIds.has(var_decl.id)) {
......@@ -1189,6 +1198,13 @@ fn transformBlockStatement(
11891198 return null;
11901199
11911200 if (!is_implicit_return_expression and result != .void) {
1201 if (t.ast.nodeTag(node) == .id) {
1202 try t.report.warn(.dangling_identifier, .{
1203 .node = node,
1204 });
1205 return result;
1206 }
1207
11921208 return try t.addInstRef(.{
11931209 .tag = .ensure_void,
11941210 .data = EncodedData.encode(.ensure_void, .{
......@@ -1203,8 +1219,6 @@ fn transformBlockStatement(
12031219 }
12041220}
12051221
1206// ---
1207
12081222fn addRootInst(t: *Transform, inst: Inst) !Inst.Index {
12091223 try t.insts.append(t.gpa, inst);
12101224 return @enumFromInt(t.insts.len - 1);
src/compiler/main.zig+4-111
......@@ -131,7 +131,10 @@ pub fn main() !void {
131131 try teg.dumpToWriter(stderr);
132132 }
133133
134 try comp.semaEntryPoint(main_file);
134 comp.semaEntryPoint(main_file) catch |err| switch (err) {
135 error.AnalysisFail => std.process.exit(1),
136 else => return err,
137 };
135138
136139 const output = std.io.getStdOut().writer();
137140 var bufw = std.io.bufferedWriter(output);
......@@ -140,114 +143,4 @@ pub fn main() !void {
140143 try bufw.flush();
141144
142145 std.process.cleanExit();
143
144 // var error_pos: u32 = 0;
145 // var tokens = Tokenizer.tokenize(source, alloc.allocator(), &error_pos) catch |err| {
146 // var line: usize = 1;
147 // var byte: usize = 0;
148 // var line_data: []const u8 = undefined;
149 // while (true) {
150 // const b = std.mem.indexOfScalarPos(u8, source, byte, '\n') orelse {
151 // line_data = source[byte..];
152 // break;
153 // };
154 // if (b > error_pos) {
155 // line_data = source[byte..b];
156 // break;
157 // }
158 // byte = b + 1;
159 // line += 1;
160 // }
161 // var col: usize = 1;
162 // var arrow_pos: usize = 0;
163 // while (byte < error_pos) {
164 // const b = source[byte];
165 // std.debug.assert(b != '\n');
166 // byte += std.unicode.utf8ByteSequenceLength(b) catch break;
167 // col += 1;
168
169 // arrow_pos += 1; // TODO: grapheme cluster
170 // }
171
172 // const stderr = std.io.getStdErr();
173 // const config = std.io.tty.detectConfig(stderr);
174 // var buf = std.io.bufferedWriter(stderr.writer());
175 // const writer = buf.writer();
176
177 // try config.setColor(writer, .red);
178 // try writer.writeAll("error");
179 // try config.setColor(writer, .bright_black);
180 // try writer.writeAll(": ");
181 // try config.setColor(writer, .reset);
182 // switch (err) {
183 // error.OutOfMemory => try writer.writeAll("Out of Memory"),
184 // error.InvalidUtf8 => try writer.writeAll("Invalid UTF-8"),
185 // error.InvalidSyntax => try writer.writeAll("Syntax Error"),
186 // error.TrailingNumericSeparator => try writer.writeAll("Trailing Numeric Separator"),
187 // error.UnterminatedString => try writer.writeAll("Unterminated String"),
188 // error.InvalidEscapeSequence => try writer.writeAll("Invalid Escape Sequence"),
189 // error.InvalidEscapeCodepoint => try writer.writeAll("Invalid Escape Codepoint"),
190 // error.GitConflict => try writer.writeAll("Found Git Conflict"),
191 // }
192 // try writer.print(" at {d}:{d}\n ", .{ line, col });
193 // try writer.writeAll(line_data);
194 // try writer.writeAll("\n ");
195 // try writer.writeByteNTimes(' ', col);
196
197 // try config.setColor(writer, .bright_green);
198 // try writer.writeAll("^");
199 // try config.setColor(writer, .reset);
200 // try writer.writeAll("\n");
201
202 // try buf.flush();
203 // return err;
204 // };
205 // defer tokens.deinit(alloc.allocator());
206
207 // // std.debug.print("tokens.len = {d}\n", .{tokens.len});
208 // // std.debug.print("token list bytes = {d}\n", .{tokens.toMultiArrayList().allocatedBytes().len});
209 // // std.debug.print("source bytes = {d}\n", .{source.len});
210 // // for (
211 // // tokens.items(.tag),
212 // // tokens.items(.offset),
213 // // ) |tag, offset| {
214 // // std.debug.print(".tag = {s} .offset = {d}\n", .{
215 // // @tagName(tag),
216 // // offset,
217 // // });
218 // // }
219
220 // const ast = ast: {
221 // var p = Parse.init(alloc.allocator(), source, tokens);
222 // errdefer p.deinit();
223 // try p.nodes.ensureUnusedCapacity(alloc.allocator(), 100);
224 // p.parseRoot() catch |err| {
225 // const tok = p.fullToken(p.pos);
226
227 // std.debug.print("ast_error = {?}\n", .{p.ast_error});
228
229 // std.debug.print("tok: {s} - '{s}' - {d}\n", .{
230 // @tagName(tok.tag),
231 // tok.slice(p.source),
232 // tok.offset,
233 // });
234
235 // return err;
236 // };
237 // break :ast try p.finalize(tokens);
238 // };
239
240 // try ast.dumpToWriter(std.io.getStdOut().writer());
241
242 // const teg = try Transform.transform(ast, alloc.allocator());
243 // std.debug.print("teg.insts.len = {d}\n", .{teg.insts.len});
244 // std.debug.print("teg.string_bytes.len = {d}\n", .{teg.string_bytes.len});
245 // std.debug.print("teg.extra.len = {d}\n", .{teg.extra.len});
246
247 // for (teg.insts.items(.tag), teg.insts.items(.data)) |tag, data| {
248 // std.debug.print("{s} - {}\n", .{
249 // @tagName(tag),
250 // data,
251 // });
252 // }
253146}