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