tiny.pluck.toplevel.script
Defined in toplevel.
API (10)
Actions
Public operations.
Types and contracts
Public types and contracts.
Source
Source: lib/pluck/src/toplevel/root.zig:7
zig
pub const script = @import("script.zig");Source: lib/pluck/src/toplevel/script.zig
zig
const std = @import("std");const pluck = @import("../root.zig");const Allocator = std.mem.Allocator;const pexpr = pluck.pexpr;const context_owner = @import("context.zig");const ToplevelContext = context_owner.ToplevelContext;const top_types = @import("types.zig");const ToplevelError = top_types.ToplevelError;const SourceLocation = top_types.SourceLocation;const SourceSpan = top_types.SourceSpan;const result_owner = @import("result.zig");const QueryResult = result_owner.QueryResult;const forms_owner = @import("forms.zig");const source_owner = @import("source.zig");pub const FormKind = enum { definition, type_definition, query, expression,};pub const SourceForm = struct { kind: FormKind, source: []const u8, name: ?[]const u8, span: SourceSpan, location: SourceLocation,};pub const SourceForms = struct { allocator: Allocator, items: []SourceForm, pub fn deinit(self: *SourceForms) void { self.allocator.free(self.items); self.items = &[_]SourceForm{}; }};pub const QueryRecord = struct { form_index: usize, form: SourceForm, result: QueryResult,};pub const Execution = struct { allocator: Allocator, forms: []SourceForm, queries: []QueryRecord, pub fn deinit(self: *Execution) void { for (self.queries) |*query| { query.result.deinit(); } self.allocator.free(self.queries); self.allocator.free(self.forms); self.queries = &[_]QueryRecord{}; self.forms = &[_]SourceForm{}; }};pub fn parseSource(allocator: Allocator, source: []const u8) ToplevelError!SourceForms { return scanSource(allocator, source, null);}pub fn parseSourceForms(self: *ToplevelContext, allocator: Allocator, source: []const u8) ToplevelError!SourceForms { self.last_error_span = null; return scanSource(allocator, source, &self.last_error_span);}pub fn executeSource(self: *ToplevelContext, allocator: Allocator, source: []const u8) ToplevelError!Execution { var parsed = try parseSourceForms(self, allocator, source); errdefer parsed.deinit(); const prebound_defs = try source_owner.prebindSexprDefs(self, source); defer if (prebound_defs.len > 0) self.allocator.free(prebound_defs); errdefer source_owner.rollbackPreboundDefs(self, prebound_defs); var queries: std.ArrayListUnmanaged(QueryRecord) = .empty; errdefer { for (queries.items) |*query| { query.result.deinit(); } queries.deinit(allocator); } for (parsed.items, 0..) |form, form_index| { const maybe_result = forms_owner.processFormSexpr(self, form.source) catch |err| { self.last_error_span = form.span; return err; }; if (maybe_result) |result| { var query_result = result; query_result.source_location = form.location; queries.append(allocator, .{ .form_index = form_index, .form = form, .result = query_result, }) catch |err| { query_result.deinit(); return switch (err) { error.OutOfMemory => ToplevelError.OutOfMemory, }; }; } } return .{ .allocator = allocator, .forms = parsed.items, .queries = queries.toOwnedSlice(allocator) catch return ToplevelError.OutOfMemory, };}fn scanSource(allocator: Allocator, source: []const u8, error_span: ?*?SourceSpan) ToplevelError!SourceForms { var forms: std.ArrayListUnmanaged(SourceForm) = .empty; errdefer forms.deinit(allocator); var pos: usize = 0; while (pos < source.len) { pos = source_owner.skipWhitespaceAndComments(source, pos); if (pos >= source.len) break; const form_start = pos; const form_end = source_owner.findFormEnd(source, pos) orelse { if (error_span) |out| out.* = source_owner.spanFromOffsets(source, form_start, source.len); return ToplevelError.ParseError; }; pos = form_end; const form_source = source[form_start..form_end]; if (form_source.len == 0) continue; const tokens = pexpr.tokenize(allocator, form_source) catch return ToplevelError.OutOfMemory; defer pexpr.freeTokens(allocator, tokens); if (tokens.len == 0) continue; const span = source_owner.spanFromOffsets(source, form_start, form_end); try forms.append(allocator, .{ .kind = classify(tokens), .source = form_source, .name = formName(tokens), .span = span, .location = source_owner.locationFromSpan(span), }); } return .{ .allocator = allocator, .items = forms.toOwnedSlice(allocator) catch return ToplevelError.OutOfMemory, };}fn classify(tokens: []const pexpr.Token) FormKind { if (tokens.len >= 2 and std.mem.eql(u8, tokens[0], "(")) { if (std.mem.eql(u8, tokens[1], "define")) return .definition; if (std.mem.eql(u8, tokens[1], "define-type")) return .type_definition; if (std.mem.eql(u8, tokens[1], "query")) return .query; } return .expression;}fn formName(tokens: []const pexpr.Token) ?[]const u8 { return switch (classify(tokens)) { .definition => definitionName(tokens), .type_definition => if (tokens.len >= 3 and source_owner.isIdentifier(tokens[2])) tokens[2] else null, .query => queryName(tokens), .expression => null, };}fn definitionName(tokens: []const pexpr.Token) ?[]const u8 { if (tokens.len < 4) return null; if (std.mem.eql(u8, tokens[2], "(")) { if (tokens.len >= 4 and source_owner.isIdentifier(tokens[3])) return tokens[3]; return null; } if (source_owner.isIdentifier(tokens[2])) return tokens[2]; return null;}fn queryName(tokens: []const pexpr.Token) ?[]const u8 { if (tokens.len <= 4) return null; if (source_owner.isIdentifier(tokens[2]) and !std.mem.eql(u8, tokens[3], ")")) return tokens[2]; return null;}test "parseSource classifies top-level forms" { const source = \\(define p 0.5) \\(define-type coin (Heads) (Tails)) \\(query coin-query (Marginal (flip p))) \\(Marginal True) ; var parsed = try parseSource(std.testing.allocator, source); defer parsed.deinit(); try std.testing.expectEqual(@as(usize, 4), parsed.items.len); try std.testing.expectEqual(FormKind.definition, parsed.items[0].kind); try std.testing.expectEqualStrings("p", parsed.items[0].name.?); try std.testing.expectEqual(FormKind.type_definition, parsed.items[1].kind); try std.testing.expectEqualStrings("coin", parsed.items[1].name.?); try std.testing.expectEqual(FormKind.query, parsed.items[2].kind); try std.testing.expectEqualStrings("coin-query", parsed.items[2].name.?); try std.testing.expectEqual(FormKind.expression, parsed.items[3].kind); try std.testing.expect(parsed.items[3].name == null);}test "parseSourceForms records unmatched form span" { var ctx = try ToplevelContext.init(std.testing.allocator); defer ctx.deinit(); try std.testing.expectError(ToplevelError.ParseError, ctx.parseSourceForms(std.testing.allocator, "(Marginal True")); const span = ctx.lastErrorSpan().?; try std.testing.expectEqual(@as(usize, 0), span.start.offset); try std.testing.expectEqual(@as(usize, 14), span.end.offset);}test "executeSource collects query records" { var ctx = try ToplevelContext.init(std.testing.allocator); defer ctx.deinit(); const source = \\(define p 0.5) \\(Marginal (flip p)) ; var execution = try ctx.executeSource(std.testing.allocator, source); defer execution.deinit(); try std.testing.expectEqual(@as(usize, 2), execution.forms.len); try std.testing.expectEqual(@as(usize, 1), execution.queries.len); try std.testing.expectEqual(@as(usize, 1), execution.queries[0].form_index); try std.testing.expectEqual(FormKind.expression, execution.queries[0].form.kind); try std.testing.expectEqual(@as(u32, 1), execution.queries[0].result.source_location.?.start_line); var found_true = false; for (execution.queries[0].result.outcomes) |outcome| { if (std.mem.eql(u8, outcome.value_str, "True")) { found_true = true; try std.testing.expectApproxEqAbs(@as(f64, 0.5), outcome.probability, 1e-9); } } try std.testing.expect(found_true);}Audit
| Definitions | 11 |
|---|---|
| Public names | 19 |
| Members | 17 |
| Version | 26.7.0 |
| Revision | daab053ee433 |