lib/std/lang/sexpr.rad 6.3 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
/// Shorthand for creating a symbol.
57
pub fn sym(s: *[u8]) -> Expr {
58
    return Expr::Sym(s);
59
}
60
61
/// Shorthand for creating a string literal.
62
pub fn str(s: *[u8]) -> Expr {
63
    return Expr::Str(s);
64
}
65
66
/// Shorthand for creating a list.
67
pub fn list(a: *mut alloc::Arena, head: *[u8], tail: *[Expr]) -> Expr {
68
    return Expr::List { head, tail: allocItems(a, tail), multiline: false };
69
}
70
71
/// Shorthand for creating a bracket-delimited vector.
72
pub fn vec(a: *mut alloc::Arena, items: *[Expr]) -> Expr {
73
    return Expr::Vec { items: allocItems(a, items) };
74
}
75
76
/// Shorthand for creating a block with inline items and child expressions.
77
pub fn block(a: *mut alloc::Arena, name: *[u8], items: *[Expr], children: *[Expr]) -> Expr {
78
    return Expr::Block { name, items: allocItems(a, items), children: allocItems(a, children) };
79
}
80
81
/// Write a string to the output target.
82
pub fn write(out: *mut Output, s: *[u8]) {
83
    match *out {
84
        case Output::Stdout => io::print(s),
85
        case Output::Buffer { buf, pos } => {
86
            let remaining = buf.len - *pos;
87
            let toWrite = remaining if s.len > remaining else s.len;
88
            for i in 0..toWrite {
89
                buf[*pos] = s[i];
90
                *pos += 1;
91
            }
92
        }
93
    }
94
}
95
96
/// Emit indentation to the output target.
97
fn indentTo(out: *mut Output, depth: u32) {
98
    for _ in 0..depth {
99
        write(out, "  ");
100
    }
101
}
102
103
/// Print a single character with escaping to the output target.
104
pub fn printEscapedTo(out: *mut Output, c: u8) {
105
    match c {
106
        case '\n' => write(out, "\\n"),
107
        case '\r' => write(out, "\\r"),
108
        case '\t' => write(out, "\\t"),
109
        case '\\' => write(out, "\\\\"),
110
        case '\'' => write(out, "\\'"),
111
        else => write(out, &[c]),
112
    }
113
}
114
115
/// Print a quoted string with escape sequences to the output target.
116
pub fn printStringTo(out: *mut Output, s: *[u8]) {
117
    write(out, "\"");
118
    for i in 0..s.len {
119
        printEscapedTo(out, s[i]);
120
    }
121
    write(out, "\"");
122
}
123
124
/// Print a character literal with escape sequences to the output target.
125
pub fn printCharTo(out: *mut Output, c: u8) {
126
    write(out, "'");
127
    printEscapedTo(out, c);
128
    write(out, "'");
129
}
130
131
/// Print an S-expression to the given output target at the given depth.
132
pub fn printTo(expr: Expr, depth: u32, out: *mut Output) {
133
    match expr {
134
        case Expr::Null => {},
135
        case Expr::Sym(s) => write(out, s),
136
        case Expr::Str(s) => printStringTo(out, s),
137
        case Expr::Char(c) => printCharTo(out, c),
138
        case Expr::List { head, tail, multiline } => {
139
            write(out, "(");
140
            write(out, head);
141
            if multiline {
142
                for i in 0..tail.len {
143
                    if tail[i] != Expr::Null {
144
                        write(out, "\n");
145
                        indentTo(out, depth + 1);
146
                        printTo(tail[i], depth + 1, out);
147
                    }
148
                }
149
            } else {
150
                let mut first = head.len == 0;
151
                for i in 0..tail.len {
152
                    if tail[i] != Expr::Null {
153
                        if first {
154
                            first = false;
155
                        } else {
156
                            write(out, " ");
157
                        }
158
                        printTo(tail[i], depth, out);
159
                    }
160
                }
161
            }
162
            write(out, ")");
163
        }
164
        case Expr::Vec { items } => {
165
            write(out, "[");
166
            for item, i in items {
167
                if item != Expr::Null {
168
                    if i > 0 {
169
                        write(out, " ");
170
                    }
171
                    printTo(item, depth, out);
172
                }
173
            }
174
            write(out, "]");
175
        }
176
        case Expr::Block { name, items, children } => {
177
            write(out, "(");
178
            write(out, name);
179
            for i in 0..items.len {
180
                if items[i] != Expr::Null {
181
                    write(out, " ");
182
                    printTo(items[i], depth, out);
183
                }
184
            }
185
            for i in 0..children.len {
186
                if children[i] != Expr::Null {
187
                    write(out, "\n");
188
                    indentTo(out, depth + 1);
189
                    printTo(children[i], depth + 1, out);
190
                }
191
            }
192
            write(out, ")");
193
        }
194
    }
195
}
196
197
/// Print an S-expression to stdout at the given indentation depth.
198
pub fn print(expr: Expr, depth: u32) {
199
    let mut out = Output::Stdout;
200
    printTo(expr, depth, &mut out);
201
}
202
203
/// Emit indentation for `depth` levels to stdout.
204
pub fn indent(depth: u32) {
205
    let mut out = Output::Stdout;
206
    indentTo(&mut out, depth);
207
}
208