lib/std/lang/sexpr.rad 6.7 KiB raw
1
//! S-expression data structure and printer.
2
//!
3
//! Provides a data structure for representing S-expressions and functions
4
//! to print them with proper formatting. Uses an arena allocator for storage.
5
6
use std::io;
7
use std::lang::alloc;
8
9
/// Output target for S-expression printing.
10
export trait Output {
11
    /// Write text to the output target.
12
    fn (&mut Output) write(s: &[u8]);
13
}
14
15
/// Print to stdout.
16
export record Stdout {}
17
18
/// Write to a buffer, tracking position.
19
export record Buffer: 'output {
20
    /// Storage for the printed text.
21
    buf: &'output mut [u8],
22
    /// Number of bytes written.
23
    pos: u32,
24
}
25
26
instance Output for Stdout {
27
    fn (out: &mut Stdout) write(s: &[u8]) {
28
        io::print(s);
29
    }
30
}
31
32
instance Output for Buffer 'output {
33
    fn (out: &mut Buffer 'output) write(s: &[u8]) {
34
        assert out.pos <= out.buf.len;
35
        let remaining = out.buf.len - out.pos;
36
        let toWrite = remaining if s.len > remaining else s.len;
37
        for i in 0..toWrite {
38
            set out.buf[out.pos] = s[i];
39
            set out.pos += 1;
40
        }
41
    }
42
}
43
44
/// An S-expression element.
45
export union Expr: Copy {
46
    /// An empty expression.
47
    Null,
48
    /// A symbol/identifier.
49
    Sym(*[u8]),
50
    /// A quoted string literal.
51
    Str(*[u8]),
52
    /// A character literal.
53
    Char(u8),
54
    /// A list with a head and tail. If `multiline` is `true`, items are printed one per line.
55
    List { head: *[u8], tail: *[Expr], multiline: bool },
56
    /// A bracket-delimited list `[...]` for block parameters and arguments.
57
    Vec { items: *[Expr] },
58
    /// A block with a name, inline items, and child statements on separate lines.
59
    Block { name: *[u8], items: *[Expr], children: *[Expr] },
60
}
61
62
/// Allocate an array of Expr in the arena.
63
export unsafe fn allocExprs(arena: &mut alloc::Arena, len: u32) -> *mut [Expr] throws (alloc::AllocError) {
64
    if len == 0 {
65
        throw alloc::AllocError::OutOfMemory;
66
    }
67
    let ptr = try alloc::allocSlice(arena, @sizeOf(Expr), @alignOf(Expr), len);
68
    return ptr as *mut [Expr];
69
}
70
71
/// Allocate and copy items into the arena.
72
export unsafe fn allocItems(a: &mut alloc::Arena, items: &[Expr]) -> *[Expr] {
73
    if items.len == 0 {
74
        return &[];
75
    }
76
    let buf = try! allocExprs(a, items.len);
77
    for item, i in items {
78
        set buf[i] = item;
79
    }
80
    return buf;
81
}
82
83
/// Shorthand for creating a symbol.
84
export fn sym(s: *[u8]) -> Expr {
85
    return Expr::Sym(s);
86
}
87
88
/// Shorthand for creating a string literal.
89
export fn str(s: *[u8]) -> Expr {
90
    return Expr::Str(s);
91
}
92
93
/// Shorthand for creating a list.
94
export unsafe fn list(a: &mut alloc::Arena, head: *[u8], tail: &[Expr]) -> Expr {
95
    return Expr::List { head, tail: allocItems(a, tail), multiline: false };
96
}
97
98
/// Shorthand for creating a bracket-delimited vector.
99
export unsafe fn vec(a: &mut alloc::Arena, items: &[Expr]) -> Expr {
100
    return Expr::Vec { items: allocItems(a, items) };
101
}
102
103
/// Shorthand for creating a block with inline items and child expressions.
104
export unsafe fn block(a: &mut alloc::Arena, name: *[u8], items: &[Expr], children: &[Expr]) -> Expr {
105
    return Expr::Block { name, items: allocItems(a, items), children: allocItems(a, children) };
106
}
107
108
/// Write a string to the output target.
109
export fn write(out: &mut opaque Output, s: &[u8]) {
110
    out.write(s);
111
}
112
113
/// Emit indentation to the output target.
114
fn indentTo(out: &mut opaque Output, depth: u32) {
115
    for _ in 0..depth {
116
        write(out, "  ");
117
    }
118
}
119
120
/// Print a single character with escaping to the output target.
121
export fn printEscapedTo(out: &mut opaque Output, c: u8) {
122
    match c {
123
        case '\n' => write(out, "\\n"),
124
        case '\r' => write(out, "\\r"),
125
        case '\t' => write(out, "\\t"),
126
        case '\\' => write(out, "\\\\"),
127
        case '\'' => write(out, "\\'"),
128
        else => write(out, &[c]),
129
    }
130
}
131
132
/// Print a quoted string with escape sequences to the output target.
133
export fn printStringTo(out: &mut opaque Output, s: &[u8]) {
134
    write(out, "\"");
135
    for i in 0..s.len {
136
        printEscapedTo(out, s[i]);
137
    }
138
    write(out, "\"");
139
}
140
141
/// Print a character literal with escape sequences to the output target.
142
export fn printCharTo(out: &mut opaque Output, c: u8) {
143
    write(out, "'");
144
    printEscapedTo(out, c);
145
    write(out, "'");
146
}
147
148
/// Print an S-expression to the given output target at the given depth.
149
export fn printTo(expr: Expr, depth: u32, out: &mut opaque Output) {
150
    match expr {
151
        case Expr::Null => {},
152
        case Expr::Sym(s) => write(out, s),
153
        case Expr::Str(s) => printStringTo(out, s),
154
        case Expr::Char(c) => printCharTo(out, c),
155
        case Expr::List { head, tail, multiline } => {
156
            write(out, "(");
157
            write(out, head);
158
            if multiline {
159
                for i in 0..tail.len {
160
                    if tail[i] <> Expr::Null {
161
                        write(out, "\n");
162
                        indentTo(out, depth + 1);
163
                        printTo(tail[i], depth + 1, out);
164
                    }
165
                }
166
            } else {
167
                let mut first = head.len == 0;
168
                for i in 0..tail.len {
169
                    if tail[i] <> Expr::Null {
170
                        if first {
171
                            set first = false;
172
                        } else {
173
                            write(out, " ");
174
                        }
175
                        printTo(tail[i], depth, out);
176
                    }
177
                }
178
            }
179
            write(out, ")");
180
        }
181
        case Expr::Vec { items } => {
182
            write(out, "[");
183
            for item, i in items {
184
                if item <> Expr::Null {
185
                    if i > 0 {
186
                        write(out, " ");
187
                    }
188
                    printTo(item, depth, out);
189
                }
190
            }
191
            write(out, "]");
192
        }
193
        case Expr::Block { name, items, children } => {
194
            write(out, "(");
195
            write(out, name);
196
            for i in 0..items.len {
197
                if items[i] <> Expr::Null {
198
                    write(out, " ");
199
                    printTo(items[i], depth, out);
200
                }
201
            }
202
            for i in 0..children.len {
203
                if children[i] <> Expr::Null {
204
                    write(out, "\n");
205
                    indentTo(out, depth + 1);
206
                    printTo(children[i], depth + 1, out);
207
                }
208
            }
209
            write(out, ")");
210
        }
211
    }
212
}
213
214
/// Print an S-expression to stdout at the given indentation depth.
215
export fn print(expr: Expr, depth: u32) {
216
    let mut out = Stdout {};
217
    printTo(expr, depth, &mut out);
218
}
219
220
/// Emit indentation for `depth` levels to stdout.
221
export fn indent(depth: u32) {
222
    let mut out = Stdout {};
223
    indentTo(&mut out, depth);
224
}