From 3182898a7bab584aa83b3f357682909da71e2eb8 Mon Sep 17 00:00:00 2001 From: Jamie Brandon Date: Wed, 1 Sep 2021 20:05:14 -0700 Subject: [PATCH] First pass at watches --- bin/run.zig | 2 +- lib/imp/lang.zig | 48 ++++++++++++++++++++++++--------- lib/imp/lang/pass/interpret.zig | 16 ++++++++++- lib/imp/lang/repr/value.zig | 4 +-- lib/imp/lang/store.zig | 31 ++++++++++++++++++++- test/end_to_end.zig | 2 +- 6 files changed, 85 insertions(+), 18 deletions(-) diff --git a/bin/run.zig b/bin/run.zig index bdfa8cc8..dbf861e3 100644 --- a/bin/run.zig +++ b/bin/run.zig @@ -35,7 +35,7 @@ pub fn main() anyerror!void { .desired_id = &desired_id, }; var error_info: ?imp.lang.InterpretErrorInfo = null; - const result = imp.lang.interpret(&arena, source.items, interrupter, &error_info); + const result = imp.lang.interpret(&arena, source.items, 0, interrupter, &error_info); const writer = std.io.getStdOut().writer(); if (result) |type_and_set| { try type_and_set.dumpInto(allocator, writer); diff --git a/lib/imp/lang.zig b/lib/imp/lang.zig index a3ea0edd..87175a02 100644 --- a/lib/imp/lang.zig +++ b/lib/imp/lang.zig @@ -61,20 +61,34 @@ pub const InterpretError = pass.parse.Error || pass.analyze.Error || pass.interpret.Error; -pub const TypeAndSet = struct { +pub const InterpretResult = struct { set_type: repr.type_.SetType, set: repr.value.Set, + watch_results: []const repr.value.Set, + watch_range: ?[2]usize, - pub fn dumpInto(self: TypeAndSet, allocator: *Allocator, out_stream: anytype) anyerror!void { + pub fn dumpInto(self: InterpretResult, allocator: *Allocator, out_stream: anytype) anyerror!void { try out_stream.writeAll("type:\n"); try self.set_type.dumpInto(out_stream); try out_stream.writeAll("\nvalue:\n"); try self.set.dumpInto(allocator, out_stream); - try out_stream.writeAll("\n"); + if (self.watch_range) |_| { + try out_stream.writeAll("\nwatch values:\n"); + for (self.watch_results) |set| { + try set.dumpInto(allocator, out_stream); + try out_stream.writeAll("\n"); + } + } } }; -pub fn interpret(arena: *ArenaAllocator, source: []const u8, interrupter: Interrupter, error_info: *?InterpretErrorInfo) InterpretError!TypeAndSet { +pub fn interpret( + arena: *ArenaAllocator, + source: []const u8, + watch_position: usize, + interrupter: Interrupter, + error_info: *?InterpretErrorInfo, +) InterpretError!InterpretResult { var store = Store.init(arena); var parse_error_info: ?pass.parse.ErrorInfo = null; @@ -101,17 +115,26 @@ pub fn interpret(arena: *ArenaAllocator, source: []const u8, interrupter: Interr return err; }; + const watch_expr_o = store.findCoreExprAt(watch_position); + var watch_range: ?[2]usize = null; + if (watch_expr_o) |watch_expr| { + const watch_meta = Store.getSyntaxMeta(Store.getCoreMeta(watch_expr).from); + watch_range = .{ watch_meta.start, watch_meta.end }; + } + var watch_results = ArrayList(repr.value.Set).init(&arena.allocator); var interpret_error_info: ?pass.interpret.ErrorInfo = null; - const set = pass.interpret.interpret(&store, arena, core_expr, interrupter, &interpret_error_info) catch |err| { + const set = pass.interpret.interpret(&store, arena, core_expr, watch_expr_o, &watch_results, interrupter, &interpret_error_info) catch |err| { if (err == error.InterpretError or err == error.NativeError) { error_info.* = .{ .Interpret = interpret_error_info.? }; } return err; }; - return TypeAndSet{ + return InterpretResult{ .set_type = set_type, .set = set, + .watch_results = watch_results.toOwnedSlice(), + .watch_range = watch_range, }; } @@ -141,6 +164,7 @@ pub const Worker = struct { pub const Request = struct { id: usize, text: []const u8, + position: usize, }; pub const Response = struct { @@ -150,7 +174,7 @@ pub const Worker = struct { }; pub const ResponseKind = union(enum) { - Ok, + Ok: ?[2]usize, Err: ?[2]usize, }; @@ -252,20 +276,20 @@ pub const Worker = struct { defer _ = gpa.deinit(); var arena = ArenaAllocator.init(&gpa.allocator); defer arena.deinit(); - var error_info: ?imp.lang.InterpretErrorInfo = null; const interrupter = Interrupter{ .current_id = new_request.id, .desired_id = &self.desired_id, }; - const result = imp.lang.interpret(&arena, new_request.text, interrupter, &error_info); + var error_info: ?imp.lang.InterpretErrorInfo = null; + const result = imp.lang.interpret(&arena, new_request.text, new_request.position, interrupter, &error_info); // print result var response_buffer = ArrayList(u8).init(self.allocator); defer response_buffer.deinit(); var response_kind: ResponseKind = undefined; - if (result) |type_and_set| { - try type_and_set.dumpInto(&arena.allocator, response_buffer.writer()); - response_kind = .Ok; + if (result) |ok| { + try ok.dumpInto(&arena.allocator, response_buffer.writer()); + response_kind = .{ .Ok = ok.watch_range }; } else |err| { try InterpretErrorInfo.dumpInto(error_info, err, response_buffer.writer()); response_kind = .{ .Err = InterpretErrorInfo.error_range(error_info, err) }; diff --git a/lib/imp/lang/pass/interpret.zig b/lib/imp/lang/pass/interpret.zig index 771b1ce8..dc6c7489 100644 --- a/lib/imp/lang/pass/interpret.zig +++ b/lib/imp/lang/pass/interpret.zig @@ -8,10 +8,12 @@ const value = imp.lang.repr.value; /// Guarantees: /// * If expr typechecks then this will not return InterpretError /// * If expr has a finite type then this will return a value.Set.Finite -pub fn interpret(store: *const Store, arena: *ArenaAllocator, expr: *const core.Expr, interrupter: imp.lang.Interrupter, error_info: *?ErrorInfo) Error!value.Set { +pub fn interpret(store: *const Store, arena: *ArenaAllocator, expr: *const core.Expr, watch_expr_o: ?*const core.Expr, watch_results: *ArrayList(value.Set), interrupter: imp.lang.Interrupter, error_info: *?ErrorInfo) Error!value.Set { var interpreter = Interpreter{ .store = store, .arena = arena, + .watch_expr_o = watch_expr_o, + .watch_results = watch_results, .scope = ArrayList(value.Scalar).init(&store.arena.allocator), .time = ArrayList(value.Time).init(&store.arena.allocator), .boxes = DeepHashMap(value.LazySet, value.Set).init(&store.arena.allocator), @@ -41,6 +43,8 @@ pub const ErrorInfo = struct { const Interpreter = struct { store: *const Store, arena: *ArenaAllocator, + watch_expr_o: ?*const core.Expr, + watch_results: *ArrayList(value.Set), scope: ArrayList(value.Scalar), time: ArrayList(value.Time), boxes: DeepHashMap(value.LazySet, value.Set), @@ -66,6 +70,16 @@ const Interpreter = struct { } fn interpret(self: *Interpreter, expr: *const core.Expr, hint: value.Tuple) Error!value.Set { + const result = self.interpretInner(expr, hint); + if (self.watch_expr_o) |watch_expr| + if (expr == watch_expr) + if (result) |set| + try self.watch_results.append(set) + else |_| {}; + return result; + } + + fn interpretInner(self: *Interpreter, expr: *const core.Expr, hint: value.Tuple) Error!value.Set { try self.interrupter.check(); switch (expr.*) { .None => { diff --git a/lib/imp/lang/repr/value.zig b/lib/imp/lang/repr/value.zig index 8cc40d61..153088c4 100644 --- a/lib/imp/lang/repr/value.zig +++ b/lib/imp/lang/repr/value.zig @@ -60,9 +60,9 @@ pub const FiniteSet = struct { } }.lessThan); if (tuples.items.len == 0) { - try out_stream.writeAll("none"); + try out_stream.writeAll("none\n"); } else if (tuples.items.len == 1 and tuples.items[0].len == 0) { - try out_stream.writeAll("some"); + try out_stream.writeAll("some\n"); } else { for (tuples.items) |tuple| { try out_stream.writeAll("| "); diff --git a/lib/imp/lang/store.zig b/lib/imp/lang/store.zig index 27e9358e..b9fef309 100644 --- a/lib/imp/lang/store.zig +++ b/lib/imp/lang/store.zig @@ -19,13 +19,15 @@ pub const CoreMeta = struct { pub const Store = struct { arena: *ArenaAllocator, next_expr_id: usize, + core_exprs: ArrayList(*const core.Expr), specializations: DeepHashMap(type_.LazySetType, ArrayList(Specialization)), pub fn init(arena: *ArenaAllocator) Store { return Store{ .arena = arena, - .specializations = DeepHashMap(type_.LazySetType, ArrayList(Specialization)).init(&arena.allocator), .next_expr_id = 0, + .core_exprs = ArrayList(*const core.Expr).init(&arena.allocator), + .specializations = DeepHashMap(type_.LazySetType, ArrayList(Specialization)).init(&arena.allocator), }; } @@ -52,6 +54,7 @@ pub const Store = struct { .id = id, }, }; + try self.core_exprs.append(&expr_and_meta.expr); return &expr_and_meta.expr; } @@ -65,6 +68,32 @@ pub const Store = struct { return &@fieldParentPtr(ExprAndMeta(core.Expr, CoreMeta), "expr", expr).meta; } + fn betterMatchForPosition(position: usize, a: *const core.Expr, b: *const core.Expr) bool { + const a_core_meta = Store.getCoreMeta(a); + const a_syntax_meta = Store.getSyntaxMeta(a_core_meta.from); + const b_core_meta = Store.getCoreMeta(b); + const b_syntax_meta = Store.getSyntaxMeta(b_core_meta.from); + // anything past position is not a match + if (a_syntax_meta.end > position) return false; + if (b_syntax_meta.end > position) return true; + // the expr that ends closest to position is the best match + if (a_syntax_meta.end > b_syntax_meta.end) return true; + if (a_syntax_meta.end < b_syntax_meta.end) return false; + // if both end at the same point, the expr that is longest is the best match + if (a_syntax_meta.start < b_syntax_meta.start) return true; + if (a_syntax_meta.start > b_syntax_meta.start) return false; + // if both have same length, return the outermost expr in the tree + return a_core_meta.id > b_core_meta.id; + } + + pub fn findCoreExprAt(self: Store, position: usize) ?*const core.Expr { + if (std.sort.min(*const core.Expr, self.core_exprs.items, position, betterMatchForPosition)) |best_match| { + const match_meta = Store.getSyntaxMeta(Store.getCoreMeta(best_match).from); + if (match_meta.end <= position) return best_match; + } + return null; + } + pub fn putSpecialization(self: *Store, lazy: type_.LazySetType, hint: []const type_.ScalarType, set_type: type_.SetType) !void { const used_hint = switch (set_type) { // always empty so can't say how much of the hint was used diff --git a/test/end_to_end.zig b/test/end_to_end.zig index 24493d0a..0799a2d2 100644 --- a/test/end_to_end.zig +++ b/test/end_to_end.zig @@ -49,7 +49,7 @@ pub fn main() anyerror!void { .desired_id = &desired_id, }; var error_info: ?imp.lang.InterpretErrorInfo = null; - const result = imp.lang.interpret(&arena, input, interrupter, &error_info); + const result = imp.lang.interpret(&arena, input, 0, interrupter, &error_info); var bytes = ArrayList(u8).init(allocator); defer bytes.deinit();