Skip to content

Commit 4e449ce

Browse files
authored
Merge pull request #16 from Cirru/one-liner
add one-liner parser APIs and clean warnings
2 parents e73c00d + 86bde8f commit 4e449ce

7 files changed

Lines changed: 161 additions & 13 deletions

File tree

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,15 @@ using @cirru_parser {type Cirru}
2121
// parse Cirru code
2222
Cirru::parse(code: String) : Array[Cirru] raise CirruParseError
2323
24+
// parse a one-line Cirru expression
25+
Cirru::parse_expr_one_liner(code: String) : Cirru raise CirruParseError
26+
2427
// format Cirru code
2528
Cirru::format(cirru: Array[Cirru], use_inline=false) : String raise FormatCirruError
29+
30+
// format one expression into one line
31+
Cirru::format_expr_one_liner(expr: Cirru) : String raise FormatCirruError
32+
Cirru::format_one_liner(expr: Cirru) : String raise FormatCirruError
2633
```
2734

2835
### License

src/parser.mbt

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ fn build_exprs(
1313
idx += 1
1414
Some(tokens[pos])
1515
}
16-
for {
16+
for ;; {
1717
let chunk = pull_token()
1818
match chunk {
1919
None => return acc
@@ -23,7 +23,7 @@ fn build_exprs(
2323
let mut pointer : Array[Cirru] = Array::new(capacity=8)
2424
// guess a nested level of 16
2525
let pointer_stack : Array[Array[Cirru]] = Array::new(capacity=16)
26-
for {
26+
for ;; {
2727
let cursor = pull_token()
2828
match cursor {
2929
None => raise CirruParseError("unexpected end of file")
@@ -68,6 +68,18 @@ pub fn Cirru::parse(code : String) -> Array[Cirru] raise CirruParseError {
6868
resolve_comma(resolve_dollar(tree))
6969
}
7070

71+
///|
72+
/// parse a one-line Cirru expression into exactly one expression
73+
pub fn Cirru::parse_expr_one_liner(
74+
code : String,
75+
) -> Cirru raise CirruParseError {
76+
let xs = Cirru::parse(code)
77+
if xs.length() != 1 {
78+
raise CirruParseError("expected 1 expression, got \{xs.length()}")
79+
}
80+
xs[0]
81+
}
82+
7183
///|
7284
suberror CirruParseError {
7385
CirruParseError(String)
@@ -280,7 +292,7 @@ fn resolve_indentations(tokens : Array[CirruLexItem]) -> Array[CirruLexItem] {
280292
let mut acc : Array[CirruLexItem] = Array::new()
281293
let mut level = 0
282294
let mut pointer = 0
283-
for {
295+
for ;; {
284296
if pointer >= size {
285297
if acc.is_empty() {
286298
return Array::new()

src/parser_test.mbt

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,91 @@ using @lib {type Cirru}
33

44
///|
55
test "parser" {
6-
assert_eq(Cirru::parse("def a"), [List([Leaf("def"), Leaf("a")])])
6+
assert_eq(Cirru::parse("def a"), [
7+
Cirru::List([Cirru::Leaf("def"), Cirru::Leaf("a")]),
8+
])
9+
}
10+
11+
///|
12+
test "parse and format one-liner expression" {
13+
let tree = Cirru::List([
14+
Cirru::Leaf("defn"),
15+
Cirru::Leaf("main"),
16+
Cirru::List([]),
17+
Cirru::List([Cirru::Leaf("println"), Cirru::Leaf("Hello, world!")]),
18+
])
19+
20+
let one_liner = try! tree.format_one_liner()
21+
assert_eq(one_liner, "defn main () $ println \"Hello, world!\"")
22+
23+
let parsed = try! Cirru::parse_expr_one_liner(one_liner)
24+
assert_eq(parsed, tree)
25+
}
26+
27+
///|
28+
test "reject multiple expressions in one-liner parser" {
29+
let result = try? Cirru::parse_expr_one_liner("a\nb")
30+
match result {
31+
Err(err) => assert_eq(err.to_string(), "expected 1 expression, got 2")
32+
Ok(_) => assert_eq("ok", "err")
33+
}
34+
}
35+
36+
///|
37+
test "format complex one-liner expression" {
38+
let tree = Cirru::List([
39+
Cirru::Leaf("a"),
40+
Cirru::List([Cirru::Leaf("b"), Cirru::List([Cirru::Leaf("c")])]),
41+
Cirru::Leaf("d"),
42+
])
43+
44+
let one_liner = try! tree.format_one_liner()
45+
assert_eq(one_liner, "a (b (c)) d")
46+
47+
let parsed = try! Cirru::parse_expr_one_liner(one_liner)
48+
assert_eq(parsed, tree)
49+
}
50+
51+
///|
52+
test "format tail expression one-liner" {
53+
let tree = Cirru::List([
54+
Cirru::Leaf("defn"),
55+
Cirru::Leaf("main"),
56+
Cirru::List([]),
57+
Cirru::List([Cirru::Leaf("println"), Cirru::Leaf("Hello")]),
58+
])
59+
60+
let one_liner = try! tree.format_one_liner()
61+
assert_eq(one_liner, "defn main () $ println Hello")
62+
63+
let parsed = try! Cirru::parse_expr_one_liner(one_liner)
64+
assert_eq(parsed, tree)
65+
}
66+
67+
///|
68+
test "format nested tail expression one-liner" {
69+
let tree = Cirru::List([
70+
Cirru::Leaf("if"),
71+
Cirru::Leaf("condition"),
72+
Cirru::List([Cirru::Leaf("do"), Cirru::List([Cirru::Leaf("action")])]),
73+
])
74+
75+
let one_liner = try! tree.format_one_liner()
76+
assert_eq(one_liner, "if condition $ do $ action")
77+
78+
let parsed = try! Cirru::parse_expr_one_liner(one_liner)
79+
assert_eq(parsed, tree)
80+
}
81+
82+
///|
83+
test "format empty tail expression one-liner" {
84+
let tree = Cirru::List([Cirru::Leaf("a"), Cirru::Leaf("b"), Cirru::List([])])
85+
86+
let one_liner = try! tree.format_one_liner()
87+
assert_eq(one_liner, "a b $")
88+
89+
let parsed = try! Cirru::parse_expr_one_liner(one_liner)
90+
assert_eq(parsed, tree)
791
}
892

993
///|

src/primes.mbt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,12 @@ pub fn Cirru::is_comment(self : Cirru) -> Bool {
149149
}
150150
}
151151

152+
///|
153+
/// format this expression into a single line of Cirru code
154+
pub fn Cirru::format_one_liner(self : Cirru) -> String raise FormatCirruError {
155+
Cirru::format_expr_one_liner(self)
156+
}
157+
152158
///|
153159
/// lexer is a simpler state machine to tokenize Cirru code
154160
priv enum CirruLexState {

src/s_expr.mbt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ fn Cirru::format_lispy_expr(
4646
chunk = "\{chunk}\{next}"
4747
}
4848
// TODO dirty way, but intuitive for now
49-
if idx < xs.length() - 1 && not(ends_with_newline(chunk)) {
49+
if idx < xs.length() - 1 && !ends_with_newline(chunk) {
5050
chunk = "\{chunk} "
5151
}
5252
}
@@ -58,7 +58,7 @@ fn Cirru::format_lispy_expr(
5858
} else {
5959
let s0 = token[0]
6060
if s0 == '|' || s0 == '"' {
61-
let sliced = (try! token[1:]).to_string()
61+
let sliced = token[1:].to_owned()
6262
"\"" + escape_string(sliced) + "\""
6363
} else if token.contains(" ") ||
6464
token.contains("\n") ||

src/tree.mbt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,18 @@ fn comma_helper(initial_after : Array[Cirru]) -> Array[Cirru] {
1212
let before : Array[Cirru] = Array::new(capacity=initial_after.length())
1313
let after : Array[Cirru] = initial_after
1414
let mut pointer = 0
15-
for {
15+
for ;; {
1616
if pointer >= after.length() {
1717
return before
1818
}
1919
match after[pointer] {
2020
List(xs) =>
21-
if not(xs.is_empty()) {
21+
if !xs.is_empty() {
2222
match xs[0] {
2323
List(_) => before.push(List(resolve_comma(xs)))
2424
Leaf(s) =>
2525
if s == "," {
26-
before.push_iter(resolve_comma(xs[1:].to_array()).iter())
26+
before.push_iter(resolve_comma(xs[1:].to_owned()).iter())
2727
} else {
2828
before.push(List(resolve_comma(xs)))
2929
}
@@ -51,7 +51,7 @@ fn dollar_helper(initial_after : Array[Cirru]) -> Array[Cirru] {
5151
let before : Array[Cirru] = Array::new(capacity=initial_after.length())
5252
let after : Array[Cirru] = initial_after
5353
let mut pointer = 0
54-
for {
54+
for ;; {
5555
if pointer >= after.length() {
5656
return before
5757
} else {

src/writer.mbt

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ fn is_char_allowed(x : Char) -> Bool {
8383
fn generate_leaf(s : String) -> String {
8484
let mut all_allowed = true
8585
for x in s {
86-
if not(is_char_allowed(x)) {
86+
if !is_char_allowed(x) {
8787
all_allowed = false
8888
break
8989
}
@@ -131,6 +131,33 @@ fn generate_inline_expr(xs : Array[Cirru]) -> String {
131131
result
132132
}
133133

134+
///|
135+
fn generate_statement_one_liner(xs : Array[Cirru]) -> String {
136+
let mut ret = ""
137+
let size = xs.length()
138+
for idx, cursor in xs {
139+
if idx > 0 {
140+
ret += " "
141+
}
142+
let at_tail = idx > 0 && idx == size - 1
143+
match cursor {
144+
Leaf(s) => ret += generate_leaf(s)
145+
List(ys) =>
146+
if at_tail {
147+
if ys.is_empty() {
148+
ret += "$"
149+
} else {
150+
ret += "$ "
151+
ret += generate_statement_one_liner(ys)
152+
}
153+
} else {
154+
ret += generate_inline_expr(ys)
155+
}
156+
}
157+
}
158+
ret
159+
}
160+
134161
///|
135162
/// by 2 spaces
136163
fn push_spaces(buf : String, n : Int) -> String {
@@ -170,7 +197,7 @@ fn Cirru::get_node_kind(self : Cirru) -> WriterNode {
170197
///|
171198
pub(all) suberror FormatCirruError {
172199
FormatCirruError(String)
173-
} derive(Show)
200+
} derive(Debug)
174201

175202
///|
176203
fn generate_tree(
@@ -188,7 +215,7 @@ fn generate_tree(
188215
let next_level = level + 1
189216
let child_insist_head = prev_kind == BoxedExpr || prev_kind == Expr
190217
let at_tail = idx != 0 &&
191-
not(in_tail) &&
218+
!in_tail &&
192219
prev_kind == Leaf &&
193220
idx == xs.length() - 1
194221

@@ -343,3 +370,15 @@ pub fn Cirru::format(
343370
) -> String raise FormatCirruError {
344371
generate_statements(xs, use_inline~)
345372
}
373+
374+
///|
375+
/// format a single Cirru expression as a single line
376+
pub fn Cirru::format_expr_one_liner(
377+
expr : Cirru,
378+
) -> String raise FormatCirruError {
379+
match expr {
380+
Leaf(_) =>
381+
raise FormatCirruError("format_expr_one_liner expects an expr (list)")
382+
List(xs) => generate_statement_one_liner(xs)
383+
}
384+
}

0 commit comments

Comments
 (0)