Skip to documentation
SLOP

tiny.pluck.toplevel.source

Reference tiny.pluck toplevel source

Defined in toplevel.

API (21)

Actions

Public operations.

Types and contracts

Public types and contracts.

No direct callersNo direct callstoplevelsource
Static calls · unresolved targets: unknown · external targets: unknown.

Source

Called byCallsNo direct callstoplevel.sourceprebindSexprDefstoplevel.sourceprocessSourceSexprNoWritertoplevel.sourceprocessSourceSexprWithCallbacktoplevel.sourceprocessSourceSexprWithWritertoplevel.sourcefindFormEnd
Static calls · unresolved targets: 1 · external targets: 0.
Called byCallsNo direct callstest sourcelib.pluck.src.toplevel.source.test_constructo...jl reference spellingstoplevel.sourceisConstructorName
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsprivate sourcelib.pluck.src.toplevel.sourceextractSexprDefNameprivate sourcelib.pluck.src.toplevel.sourceisOperatorIdentifierStarttoplevel.sourceisIdentifier
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstoplevel.sourceprocessSourceSexprWithCallbacktoplevel.sourcelocationFromSpantoplevel.sourcespanFromOffsetstoplevel.sourcelocationFromOffsets
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstoplevel.sourcelocationFromOffsetstoplevel.sourcelocationFromSpan
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callstoplevel.sourcespanFromOffsetstoplevel.sourceoffsetToLineCol
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallsNo direct callerspexprfreeTokenspexprtokenizeprivate sourcelib.pluck.src.toplevel.sourceextractSexprDefNametoplevel.sourcefindFormEndtoplevel.sourceskipWhitespaceAndCommentstoplevel.sourceprebindSexprDefs
Static calls · unresolved targets: 3 · external targets: 2.
Called byCallsNo direct callerstoplevel.sourcefindFormEndtoplevel.sourceskipWhitespaceAndCommentstoplevel.sourcespanFromOffsetstoplevel.sourceprocessSourceSexprNoWriter
Static calls · unresolved targets: 2 · external targets: 2.
Called byCallsNo direct callerstoplevel.sourcefindFormEndtoplevel.sourcelocationFromOffsetstoplevel.sourceskipWhitespaceAndCommentstoplevel.sourcespanFromOffsetstoplevel.sourceprocessSourceSexprWithCallback
Static calls · unresolved targets: 3 · external targets: 2.
Called byCallsNo direct callerstoplevel.sourcefindFormEndtoplevel.sourceskipWhitespaceAndCommentstoplevel.sourcespanFromOffsetstoplevel.sourceprocessSourceSexprWithWriter
Static calls · unresolved targets: 3 · external targets: 3.
Called byCallsNo direct callstoplevel.sourceprebindSexprDefstoplevel.sourceprocessSourceSexprNoWritertoplevel.sourceprocessSourceSexprWithCallbacktoplevel.sourceprocessSourceSexprWithWritertoplevel.sourceskipWhitespaceAndComments
Static calls · unresolved targets: 0 · external targets: 0.
Called byCallstoplevel.sourcelocationFromOffsetstoplevel.sourceprocessSourceSexprNoWritertoplevel.sourceprocessSourceSexprWithCallbacktoplevel.sourceprocessSourceSexprWithWritertoplevel.sourceoffsetToLineColtoplevel.sourcespanFromOffsets
Static calls · unresolved targets: 0 · external targets: 0.

Source: lib/pluck/src/toplevel/root.zig:15

zig
pub const source = @import("source.zig");

Source: lib/pluck/src/toplevel/source.zig

zig
const std = @import("std");const sys = @import("sys");const pluck = @import("../root.zig");const Allocator = std.mem.Allocator;const pexpr = pluck.pexpr;const PExpr = pexpr.PExpr;const Symbol = pexpr.Symbol;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");pub fn loadFile(self: *ToplevelContext, path: []const u8) ToplevelError!void {    const source = sys.fs.readFileAlloc(self.allocator, path, 10 * 1024 * 1024) catch |err| switch (err) {        error.FileNotFound => return ToplevelError.FileNotFound,        else => return ToplevelError.IoError,    };    defer self.allocator.free(source);    try processSource(self, source);}pub fn loadFileWithWriter(self: *ToplevelContext, path: []const u8, writer: anytype) ToplevelError!void {    const source = sys.fs.readFileAlloc(self.allocator, path, 10 * 1024 * 1024) catch |err| switch (err) {        error.FileNotFound => return ToplevelError.FileNotFound,        else => return ToplevelError.IoError,    };    defer self.allocator.free(source);    try processSourceWithWriter(self, source, writer);}pub fn processSource(self: *ToplevelContext, source: []const u8) ToplevelError!void {    try processSourceSexprNoWriter(self, source);}pub fn processSourceSexprNoWriter(self: *ToplevelContext, source: []const u8) ToplevelError!void {    const prebound_defs = try prebindSexprDefs(self, source);    defer if (prebound_defs.len > 0) self.allocator.free(prebound_defs);    errdefer rollbackPreboundDefs(self, prebound_defs);    var pos: usize = 0;    self.last_error_span = null;    while (pos < source.len) {        pos = skipWhitespaceAndComments(source, pos);        if (pos >= source.len) break;        const form_start = pos;        const form_end = findFormEnd(source, pos) orelse {            self.last_error_span = 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 result = forms_owner.processFormSexpr(self, form_source) catch |err| {            self.last_error_span = spanFromOffsets(source, form_start, form_end);            return err;        };        if (result) |*query_result| {            @constCast(query_result).deinit();        }    }}pub fn processSourceWithWriter(self: *ToplevelContext, source: []const u8, writer: anytype) ToplevelError!void {    try processSourceSexprWithWriter(self, source, writer);}pub const QueryResultCallback = *const fn (*QueryResult, *anyopaque) void;pub fn processSourceWithCallback(    self: *ToplevelContext,    source: []const u8,    callback: QueryResultCallback,    context: *anyopaque,) ToplevelError!void {    try processSourceSexprWithCallback(self, source, callback, context);}pub fn processSourceSexprWithWriter(self: *ToplevelContext, source: []const u8, writer: anytype) ToplevelError!void {    const prebound_defs = try prebindSexprDefs(self, source);    defer if (prebound_defs.len > 0) self.allocator.free(prebound_defs);    errdefer rollbackPreboundDefs(self, prebound_defs);    var pos: usize = 0;    self.last_error_span = null;    while (pos < source.len) {        pos = skipWhitespaceAndComments(source, pos);        if (pos >= source.len) break;        const form_start = pos;        const form_end = findFormEnd(source, pos) orelse {            self.last_error_span = 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 result = forms_owner.processFormSexpr(self, form_source) catch |err| {            self.last_error_span = spanFromOffsets(source, form_start, form_end);            return err;        };        if (result) |*query_result| {            defer @constCast(query_result).deinit();            query_result.print(writer) catch {};            writer.flush() catch {};        }    }}pub fn processSourceSexprWithCallback(    self: *ToplevelContext,    source: []const u8,    callback: QueryResultCallback,    context: *anyopaque,) ToplevelError!void {    const prebound_defs = try prebindSexprDefs(self, source);    defer if (prebound_defs.len > 0) self.allocator.free(prebound_defs);    errdefer rollbackPreboundDefs(self, prebound_defs);    var pos: usize = 0;    self.last_error_span = null;    while (pos < source.len) {        pos = skipWhitespaceAndComments(source, pos);        if (pos >= source.len) break;        const form_start = pos;        const form_end = findFormEnd(source, pos) orelse {            self.last_error_span = 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 source_loc = locationFromOffsets(source, form_start, form_end);        const result = forms_owner.processFormSexpr(self, form_source) catch |err| {            self.last_error_span = spanFromOffsets(source, form_start, form_end);            return err;        };        if (result) |*query_result| {            var mutable_result = query_result.*;            mutable_result.source_location = source_loc;            callback(&mutable_result, context);        }    }}pub const PreboundDef = struct {    name: Symbol,    prev_expr: ?*PExpr,    prev_is_stdlib: bool,    prev_doc: ?[]const u8,};pub fn prebindSexprDefs(self: *ToplevelContext, source: []const u8) ToplevelError![]PreboundDef {    const arena_alloc = self.arena.allocator();    var prebound: std.ArrayListUnmanaged(PreboundDef) = .empty;    errdefer prebound.deinit(self.allocator);    var seen: std.StringHashMapUnmanaged(void) = .{};    defer seen.deinit(self.allocator);    var pos: usize = 0;    while (pos < source.len) {        pos = skipWhitespaceAndComments(source, pos);        if (pos >= source.len) break;        const form_start = pos;        const form_end = findFormEnd(source, pos) orelse return ToplevelError.ParseError;        pos = form_end;        const form_source = source[form_start..form_end];        if (form_source.len == 0) continue;        const tokens = pexpr.tokenize(self.allocator, form_source) catch return ToplevelError.OutOfMemory;        defer pexpr.freeTokens(self.allocator, tokens);        if (extractSexprDefName(tokens)) |name| {            try prebindDefinition(self, arena_alloc, name, &prebound, &seen);        }    }    return prebound.toOwnedSlice(self.allocator) catch return ToplevelError.OutOfMemory;}fn extractSexprDefName(tokens: []const pexpr.Token) ?[]const u8 {    if (tokens.len < 4) return null;    if (!std.mem.eql(u8, tokens[0], "(")) return null;    if (!std.mem.eql(u8, tokens[1], "define")) return null;    if (std.mem.eql(u8, tokens[2], "(")) {        if (tokens.len >= 4 and isIdentifier(tokens[3])) {            return tokens[3];        }        return null;    }    if (isIdentifier(tokens[2])) {        return tokens[2];    }    return null;}fn prebindDefinition(    self: *ToplevelContext,    arena_alloc: Allocator,    name: []const u8,    prebound: *std.ArrayListUnmanaged(PreboundDef),    seen: *std.StringHashMapUnmanaged(void),) ToplevelError!void {    if (seen.contains(name)) return;    const duped_name = arena_alloc.dupe(u8, name) catch return ToplevelError.OutOfMemory;    try seen.put(self.allocator, duped_name, {});    const prev_def = self.definitions.lookupDefinition(duped_name);    const prev_expr = if (prev_def) |d| d.expr else null;    const prev_is_stdlib = if (prev_def) |d| d.is_stdlib else false;    const prev_doc = if (prev_def) |d| d.doc else null;    _ = self.definitions.remove(duped_name);    const dummy = PExpr.init(arena_alloc, .{ .construct = .{ .constructor = "Unit" } }) catch return ToplevelError.OutOfMemory;    self.definitions.define(duped_name, dummy) catch return ToplevelError.OutOfMemory;    prebound.append(self.allocator, .{        .name = duped_name,        .prev_expr = prev_expr,        .prev_is_stdlib = prev_is_stdlib,        .prev_doc = prev_doc,    }) catch return ToplevelError.OutOfMemory;}pub fn rollbackPreboundDefs(self: *ToplevelContext, prebound: []const PreboundDef) void {    for (prebound) |entry| {        _ = self.definitions.remove(entry.name);        if (entry.prev_expr) |expr| {            if (entry.prev_is_stdlib) {                if (entry.prev_doc) |doc| {                    self.definitions.defineStdlibWithDoc(entry.name, expr, doc) catch {};                } else {                    self.definitions.defineStdlib(entry.name, expr) catch {};                }            } else {                if (entry.prev_doc) |doc| {                    self.definitions.defineWithDoc(entry.name, expr, doc) catch {};                } else {                    self.definitions.define(entry.name, expr) catch {};                }            }        }    }}pub const LineCol = struct {    line: u32,    col: u32,};pub fn spanFromOffsets(source: []const u8, start: usize, end: usize) SourceSpan {    const start_loc = offsetToLineCol(source, start);    const end_loc = offsetToLineCol(source, end);    return .{        .start = .{            .line = start_loc.line,            .column = start_loc.col,            .offset = @min(start, source.len),        },        .end = .{            .line = end_loc.line,            .column = end_loc.col,            .offset = @min(end, source.len),        },    };}pub fn locationFromSpan(span: SourceSpan) SourceLocation {    return .{        .start_line = span.start.line,        .start_col = span.start.column,        .end_line = span.end.line,        .end_col = span.end.column,    };}pub fn locationFromOffsets(source: []const u8, start: usize, end: usize) SourceLocation {    return locationFromSpan(spanFromOffsets(source, start, end));}pub fn offsetToLineCol(source: []const u8, offset: usize) LineCol {    var line: u32 = 0;    var col: u32 = 0;    for (source[0..@min(offset, source.len)]) |c| {        if (c == '\n') {            line += 1;            col = 0;        } else {            col += 1;        }    }    return .{ .line = line, .col = col };}pub fn isIdentifier(token: []const u8) bool {    if (token.len == 0) return false;    const first = token[0];    if (!std.ascii.isAlphabetic(first) and first != '_' and !isOperatorIdentifierStart(first)) return false;    for (token[1..]) |c| {        if (!std.ascii.isAlphanumeric(c) and c != '_' and c != '-' and c != '?' and c != '=' and !isOperatorIdentifierStart(c)) return false;    }    return true;}fn isOperatorIdentifierStart(c: u8) bool {    return switch (c) {        '+', '-', '*', '/', '<', '>', '=' => true,        else => false,    };}pub fn isConstructorName(token: []const u8) bool {    if (token.len == 0) return false;    const first = token[0];    if (!std.ascii.isAlphabetic(first) and first != '_') return false;    for (token[1..]) |c| {        if (!std.ascii.isAlphanumeric(c) and c != '_' and c != '-') return false;    }    return true;}test "constructor names accept Pluck.jl reference spellings" {    try std.testing.expect(isConstructorName("a_"));    try std.testing.expect(isConstructorName("the_"));    try std.testing.expect(isConstructorName("Start"));    try std.testing.expect(isConstructorName("non-terminal"));    try std.testing.expect(!isConstructorName("+"));    try std.testing.expect(!isConstructorName("nat=?"));    try std.testing.expect(!isConstructorName(""));}test "processSource records absolute form span on form error" {    var ctx = try ToplevelContext.init(std.testing.allocator);    defer ctx.deinit();    const source_text =        \\(Marginal True)        \\(define p )    ;    try std.testing.expectError(ToplevelError.InvalidDefineForm, ctx.processSource(source_text));    const span = ctx.lastErrorSpan().?;    try std.testing.expectEqual(@as(u32, 1), span.start.line);    try std.testing.expectEqual(@as(usize, 16), span.start.offset);}pub fn skipWhitespaceAndComments(source: []const u8, start: usize) usize {    var pos = start;    while (pos < source.len) {        const c = source[pos];        if (std.ascii.isWhitespace(c)) {            pos += 1;            continue;        }        if (pos + 1 < source.len and source[pos] == ';' and source[pos + 1] == ';') {            while (pos < source.len and source[pos] != '\n') {                pos += 1;            }            continue;        }        break;    }    return pos;}pub fn findFormEnd(source: []const u8, start: usize) ?usize {    if (start >= source.len) return null;    if (source[start] != '(') {        var pos = start;        while (pos < source.len and !std.ascii.isWhitespace(source[pos]) and source[pos] != '(') {            pos += 1;        }        return pos;    }    var depth: i32 = 0;    var pos = start;    var in_string = false;    while (pos < source.len) {        const c = source[pos];        if (c == '"' and (pos == 0 or source[pos - 1] != '\\')) {            in_string = !in_string;            pos += 1;            continue;        }        if (in_string) {            pos += 1;            continue;        }        if (pos + 1 < source.len and c == ';' and source[pos + 1] == ';') {            while (pos < source.len and source[pos] != '\n') {                pos += 1;            }            continue;        }        if (c == '(') {            depth += 1;        } else if (c == ')') {            depth -= 1;            if (depth == 0) {                return pos + 1;            }        }        pos += 1;    }    return null;}

Audit

Definitions22
Public names33
Members6
Version26.7.0
Revisiondaab053ee433