Merge remote-tracking branch 'origin/main' into import-builtins

This commit is contained in:
Richard Feldman 2025-10-17 20:56:17 -04:00
commit 82c6562352
No known key found for this signature in database
623 changed files with 37254 additions and 35279 deletions

View file

@ -20,6 +20,12 @@ pub const Color = enum {
punctuation,
};
/// Controls whether line/column information is included in output
pub const LineColMode = enum {
skip_linecol,
include_linecol,
};
/// Helper function to escape HTML characters
fn escapeHtmlChar(writer: anytype, char: u8) !void {
switch (char) {
@ -255,7 +261,7 @@ pub fn endNode(self: *SExprTree, begin: NodeBegin, attrsMarker: NodeBegin) std.m
}
/// Internal method that writes the node using a writer implementation
fn toStringImpl(self: *const SExprTree, node: Node, writer_impl: anytype, indent: usize) !void {
fn toStringImpl(self: *const SExprTree, node: Node, writer_impl: anytype, indent: usize, linecol_mode: LineColMode) !void {
switch (node) {
.StaticAtom => |s| {
try writer_impl.setColor(.node_name);
@ -290,7 +296,6 @@ fn toStringImpl(self: *const SExprTree, node: Node, writer_impl: anytype, indent
},
.BytesRange => |range| {
try writer_impl.beginSourceRange(range.begin, range.end);
// try writer_impl.print("@{d}-{d}", .{ range.begin, range.end });
try writer_impl.print("@{d}.{d}-{d}.{d}", .{
// add one to display numbers instead of index
range.region.start_line_idx + 1,
@ -307,20 +312,36 @@ fn toStringImpl(self: *const SExprTree, node: Node, writer_impl: anytype, indent
var first = true;
for (range.begin..range.attrs_marker) |i| {
const child = self.children.items[i];
// Skip BytesRange nodes when linecol_mode is .skip_linecol
// Note we do this check here to prevent trailing whitespace in the output
if (child == .BytesRange and linecol_mode == .skip_linecol) {
continue;
}
if (!first) {
try writer_impl.print(" ", .{});
}
first = false;
try self.toStringImpl(self.children.items[i], writer_impl, indent + 1);
try self.toStringImpl(child, writer_impl, indent + 1, linecol_mode);
}
for (range.attrs_marker..range.end) |i| {
const child = self.children.items[i];
// Skip BytesRange nodes when linecol_mode is .skip_linecol
// Note we do this check here to prevent extra newlines in the output
if (child == .BytesRange and linecol_mode == .skip_linecol) {
continue;
}
if (!first) {
try writer_impl.print("\n", .{});
try writer_impl.writeIndent(indent + 1);
}
first = false;
try self.toStringImpl(self.children.items[i], writer_impl, indent + 1);
try self.toStringImpl(child, writer_impl, indent + 1, linecol_mode);
}
try writer_impl.setColor(.punctuation);
@ -331,24 +352,24 @@ fn toStringImpl(self: *const SExprTree, node: Node, writer_impl: anytype, indent
}
/// Pretty-print the root node (top of stack) to the writer
pub fn printTree(self: *const SExprTree, writer: anytype) !void {
pub fn printTree(self: *const SExprTree, writer: anytype, linecol_mode: LineColMode) !void {
if (self.stack.items.len == 0) return;
var plain_writer = PlainTextSExprWriter{ .writer = writer.any() };
try self.toStringImpl(self.stack.items[self.stack.items.len - 1], &plain_writer, 0);
try self.toStringImpl(self.stack.items[self.stack.items.len - 1], &plain_writer, 0, linecol_mode);
}
/// Render this SExprTree to a writer with pleasing indentation.
pub fn toStringPretty(self: *const SExprTree, writer: std.io.AnyWriter) !void {
pub fn toStringPretty(self: *const SExprTree, writer: std.io.AnyWriter, linecol_mode: LineColMode) !void {
if (self.stack.items.len == 0) return;
var plain_writer = PlainTextSExprWriter{ .writer = writer };
try self.toStringImpl(self.stack.items[self.stack.items.len - 1], &plain_writer, 0);
try self.toStringImpl(self.stack.items[self.stack.items.len - 1], &plain_writer, 0, linecol_mode);
}
/// Render this SExprTree to HTML with syntax highlighting.
pub fn toHtml(self: *const SExprTree, writer: std.io.AnyWriter) !void {
pub fn toHtml(self: *const SExprTree, writer: std.io.AnyWriter, linecol_mode: LineColMode) !void {
if (self.stack.items.len == 0) return;
var html_writer = HtmlSExprWriter.init(writer);
try self.toStringImpl(self.stack.items[self.stack.items.len - 1], &html_writer, 0);
try self.toStringImpl(self.stack.items[self.stack.items.len - 1], &html_writer, 0, linecol_mode);
html_writer.deinit() catch {
return error.ErrFinalizingHTMLWriter;
};

View file

@ -97,6 +97,55 @@ pub const NumLiteral = union(enum) {
Frac: FracLiteral,
};
/// The core allocators for the lifetime of a roc program.
///
/// This structure should be used to pass allocators to most functions in Roc.
/// Data structures should anchor to a generic allocator instead (alloc: Allocator).
/// It is up to the instanciator of the data structure to pick what it will use.
/// Generally speaking though, data structures can realloc and will use the gpa.
///
/// IMPORTANT: After initialization, Allocators must always be passed by pointer (*Allocators),
/// never by value. Passing by value will invalidate the arena allocator pointer!
pub const Allocators = struct {
/// The gpa is the general purpose allocator. Anything allocated with the gpa must be freed.
/// the gpa should generally be used for large allocations and things that might get reallocated.
/// It is best to avoid allocating small or short lived things with the gpa.
gpa: std.mem.Allocator,
/// The arena is an arena allocator that is around for the entire roc compilation.
/// The arena should be used for small and miscellaneous allocations.
/// Things allocated in arena are expected to never be freed individually.
///
/// IMPORTANT: This field contains a pointer to arena_impl. The struct must not be
/// moved after initialization, or this pointer will be invalidated.
arena: std.mem.Allocator,
/// The underlying arena allocator implementation (stored to enable deinit)
arena_impl: std.heap.ArenaAllocator,
// TODO: consider if we want to add scratch. It would be an arena reset between each compilation phase.
// scratch: ?std.mem.Allocator,
/// Initialize the Allocators in-place with a general purpose allocator.
///
/// IMPORTANT: This struct must be initialized in its final memory location.
/// After calling initInPlace(), the struct must only be passed by pointer (*Allocators),
/// never by value, or the arena allocator pointer will be invalidated.
pub fn initInPlace(self: *Allocators, gpa: std.mem.Allocator) void {
self.* = .{
.gpa = gpa,
.arena = undefined,
.arena_impl = std.heap.ArenaAllocator.init(gpa),
};
self.arena = self.arena_impl.allocator();
}
/// Deinitialize the arena allocator.
pub fn deinit(self: *Allocators) void {
self.arena_impl.deinit();
}
};
test "base tests" {
std.testing.refAllDecls(@import("CommonEnv.zig"));
std.testing.refAllDecls(@import("DataSpan.zig"));