atomy 0.1.1 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (172) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +13 -0
  3. data/LICENSE.md +201 -0
  4. data/bin/atomy +16 -133
  5. data/kernel/array.ay +6 -0
  6. data/kernel/atomy.ay +18 -0
  7. data/kernel/condition.ay +171 -271
  8. data/kernel/control-flow.ay +197 -192
  9. data/kernel/core.ay +120 -0
  10. data/kernel/data.ay +83 -39
  11. data/kernel/define.ay +84 -93
  12. data/kernel/doc.ay +282 -449
  13. data/kernel/dynamic.ay +25 -29
  14. data/kernel/file.ay +9 -0
  15. data/kernel/grammar.ay +267 -0
  16. data/kernel/hash.ay +17 -0
  17. data/kernel/interpolation.ay +59 -0
  18. data/kernel/io.ay +70 -244
  19. data/kernel/let-macro.ay +24 -0
  20. data/kernel/let-pattern.ay +24 -0
  21. data/kernel/loop.ay +80 -0
  22. data/kernel/mutation.ay +53 -0
  23. data/kernel/particles.ay +176 -39
  24. data/kernel/patterns.ay +527 -191
  25. data/kernel/pretty.ay +311 -277
  26. data/kernel/quotes.ay +29 -0
  27. data/kernel/range.ay +4 -0
  28. data/kernel/regexp.ay +23 -0
  29. data/kernel/repl.ay +83 -109
  30. data/kernel/stack-local.ay +21 -0
  31. data/lib/atomy.rb +37 -0
  32. data/lib/atomy/bootstrap.rb +256 -0
  33. data/lib/atomy/code/assign.rb +64 -0
  34. data/lib/atomy/code/block.rb +98 -0
  35. data/lib/atomy/code/class_variable.rb +17 -0
  36. data/lib/atomy/code/constant.rb +21 -0
  37. data/lib/atomy/code/define.rb +242 -0
  38. data/lib/atomy/code/define_function.rb +51 -0
  39. data/lib/atomy/code/define_method.rb +20 -0
  40. data/lib/atomy/code/false.rb +9 -0
  41. data/lib/atomy/code/instance_variable.rb +15 -0
  42. data/lib/atomy/code/integer.rb +13 -0
  43. data/lib/atomy/code/list.rb +17 -0
  44. data/lib/atomy/code/nil.rb +9 -0
  45. data/lib/atomy/code/pattern.rb +23 -0
  46. data/lib/atomy/code/pattern/and.rb +61 -0
  47. data/lib/atomy/code/pattern/quasi_quote.rb +185 -0
  48. data/lib/atomy/code/pattern/splat.rb +29 -0
  49. data/lib/atomy/code/pattern/wildcard.rb +37 -0
  50. data/lib/atomy/code/quasi_quote.rb +118 -0
  51. data/lib/atomy/code/quote.rb +13 -0
  52. data/lib/atomy/code/self.rb +9 -0
  53. data/lib/atomy/code/send.rb +110 -0
  54. data/lib/atomy/code/sequence.rb +23 -0
  55. data/lib/atomy/code/string_literal.rb +53 -0
  56. data/lib/atomy/code/symbol.rb +13 -0
  57. data/lib/atomy/code/true.rb +9 -0
  58. data/lib/atomy/code/undefined.rb +9 -0
  59. data/lib/atomy/code/variable.rb +17 -0
  60. data/lib/atomy/codeloader.rb +218 -0
  61. data/lib/atomy/compiler.rb +57 -0
  62. data/lib/atomy/errors.rb +54 -0
  63. data/lib/atomy/grammar.rb +2278 -0
  64. data/lib/atomy/locals.rb +75 -0
  65. data/lib/atomy/message_structure.rb +277 -0
  66. data/lib/atomy/method.rb +343 -0
  67. data/lib/atomy/module.rb +144 -0
  68. data/lib/atomy/node/constructable.rb +169 -0
  69. data/lib/atomy/node/equality.rb +113 -0
  70. data/lib/atomy/node/meta.rb +206 -0
  71. data/lib/atomy/node/pretty.rb +108 -0
  72. data/lib/atomy/parser.rb +21 -0
  73. data/lib/atomy/pattern.rb +26 -0
  74. data/lib/atomy/pattern/and.rb +59 -0
  75. data/lib/atomy/pattern/attribute.rb +16 -0
  76. data/lib/atomy/pattern/class_variable.rb +15 -0
  77. data/lib/atomy/pattern/equality.rb +42 -0
  78. data/lib/atomy/pattern/instance_variable.rb +15 -0
  79. data/lib/atomy/pattern/kind_of.rb +20 -0
  80. data/lib/atomy/pattern/or.rb +48 -0
  81. data/lib/atomy/pattern/quasi_quote.rb +164 -0
  82. data/lib/atomy/pattern/splat.rb +15 -0
  83. data/lib/atomy/pattern/wildcard.rb +18 -0
  84. data/lib/atomy/rubygems.rb +48 -0
  85. data/lib/atomy/version.rb +3 -0
  86. metadata +169 -134
  87. data/COPYING +0 -30
  88. data/README.md +0 -1
  89. data/kernel/block.ay +0 -30
  90. data/kernel/boot.ay +0 -10
  91. data/kernel/comparison.ay +0 -61
  92. data/kernel/concurrency.ay +0 -84
  93. data/kernel/cosmetics.ay +0 -3
  94. data/kernel/data-delta.ay +0 -105
  95. data/kernel/documentation.ay +0 -135
  96. data/kernel/errors.ay +0 -6
  97. data/kernel/format.ay +0 -13
  98. data/kernel/format/data.ay +0 -89
  99. data/kernel/format/formatter.ay +0 -345
  100. data/kernel/format/parser.ay +0 -13
  101. data/kernel/hashes.ay +0 -39
  102. data/kernel/namespaces.ay +0 -63
  103. data/kernel/node.ay +0 -48
  104. data/kernel/operators.ay +0 -28
  105. data/kernel/precision.ay +0 -148
  106. data/kernel/therie.ay +0 -204
  107. data/lib/ast/binary_send.rb +0 -44
  108. data/lib/ast/block.rb +0 -268
  109. data/lib/ast/constant.rb +0 -88
  110. data/lib/ast/internal/assign.rb +0 -19
  111. data/lib/ast/internal/block_pass.rb +0 -21
  112. data/lib/ast/internal/catch.rb +0 -247
  113. data/lib/ast/internal/class.rb +0 -30
  114. data/lib/ast/internal/class_variable.rb +0 -23
  115. data/lib/ast/internal/define.rb +0 -174
  116. data/lib/ast/internal/ensure.rb +0 -135
  117. data/lib/ast/internal/file.rb +0 -14
  118. data/lib/ast/internal/global_variable.rb +0 -20
  119. data/lib/ast/internal/if_then_else.rb +0 -24
  120. data/lib/ast/internal/instance_variable.rb +0 -17
  121. data/lib/ast/internal/let_macro.rb +0 -35
  122. data/lib/ast/internal/macro_quote.rb +0 -23
  123. data/lib/ast/internal/match.rb +0 -53
  124. data/lib/ast/internal/module.rb +0 -30
  125. data/lib/ast/internal/pattern.rb +0 -17
  126. data/lib/ast/internal/return.rb +0 -29
  127. data/lib/ast/internal/set.rb +0 -19
  128. data/lib/ast/internal/singleton_class.rb +0 -18
  129. data/lib/ast/internal/splat.rb +0 -14
  130. data/lib/ast/internal/when.rb +0 -24
  131. data/lib/ast/list.rb +0 -25
  132. data/lib/ast/macro.rb +0 -37
  133. data/lib/ast/node.rb +0 -599
  134. data/lib/ast/operator.rb +0 -21
  135. data/lib/ast/particle.rb +0 -13
  136. data/lib/ast/primitive.rb +0 -20
  137. data/lib/ast/quasi_quote.rb +0 -20
  138. data/lib/ast/quote.rb +0 -13
  139. data/lib/ast/send.rb +0 -104
  140. data/lib/ast/splice.rb +0 -32
  141. data/lib/ast/string.rb +0 -23
  142. data/lib/ast/unary.rb +0 -44
  143. data/lib/ast/unquote.rb +0 -45
  144. data/lib/ast/variable.rb +0 -64
  145. data/lib/atomy.kpeg.rb +0 -3995
  146. data/lib/code_loader.rb +0 -137
  147. data/lib/compiler/compiler.rb +0 -155
  148. data/lib/compiler/stages.rb +0 -81
  149. data/lib/formatter.kpeg.rb +0 -1394
  150. data/lib/macros.rb +0 -317
  151. data/lib/method.rb +0 -261
  152. data/lib/namespace.rb +0 -236
  153. data/lib/parser.rb +0 -28
  154. data/lib/patterns.rb +0 -276
  155. data/lib/patterns/any.rb +0 -21
  156. data/lib/patterns/attribute.rb +0 -59
  157. data/lib/patterns/block_pass.rb +0 -54
  158. data/lib/patterns/constant.rb +0 -33
  159. data/lib/patterns/default.rb +0 -44
  160. data/lib/patterns/head_tail.rb +0 -63
  161. data/lib/patterns/list.rb +0 -77
  162. data/lib/patterns/match.rb +0 -45
  163. data/lib/patterns/named.rb +0 -55
  164. data/lib/patterns/named_class.rb +0 -46
  165. data/lib/patterns/named_global.rb +0 -46
  166. data/lib/patterns/named_instance.rb +0 -46
  167. data/lib/patterns/particle.rb +0 -29
  168. data/lib/patterns/quasi_quote.rb +0 -184
  169. data/lib/patterns/quote.rb +0 -33
  170. data/lib/patterns/singleton_class.rb +0 -31
  171. data/lib/patterns/splat.rb +0 -57
  172. data/lib/util.rb +0 -37
data/kernel/data.ay CHANGED
@@ -1,56 +1,100 @@
1
- macro(&x):
2
- Atomy::AST::BlockPass new(x line, x)
1
+ -- TODO: make this work:
2
+ --
3
+ -- module:
4
+ -- data(A(@x))
5
+ -- A(x) foo := x
6
+ --
7
+ -- right now pattern definitions can only be at the toplevel
3
8
 
4
- macro(*x):
5
- Atomy::AST::Splat new(x line, x)
9
+ use(require("core"))
10
+ use(require("define"))
11
+ use(require("control-flow"))
6
12
 
7
- macro(@(x: Variable)):
8
- Atomy::AST::InstanceVariable new(x line, x name)
13
+ -- patterns defined by dsl use 'with', so make sure it's exported
14
+ export(use(require("patterns")))
9
15
 
10
- macro(@@(x: Variable)):
11
- Atomy::AST::ClassVariable new(x line, x name)
16
+ fn(define-class(root, e & Atomy Grammar AST Constant)):
17
+ define-class(root, `((~e)()))
12
18
 
13
- macro($'exception):
14
- Atomy::AST::GlobalVariable new(line, "!")
19
+ fn(define-class(root, x & `((~name)(~*as)))):
20
+ define-class(root, `(~x {}))
15
21
 
16
- macro($'path):
17
- Atomy::AST::GlobalVariable new(line, ":")
22
+ fn(define-class(root, `(~(n & Atomy Grammar AST Constant) { ~*cs }))):
23
+ define-class(root, `((~n)() { ~*cs }))
18
24
 
19
- macro($'separator):
20
- Atomy::AST::GlobalVariable new(line, "/")
25
+ fn(define-class(root, `((~name)(~*as): ~*cs))):
26
+ attrs = []
27
+ as each [x]:
28
+ x match:
29
+ (`@~_):
30
+ attrs << x
21
31
 
22
- macro($'0):
23
- Atomy::AST::GlobalVariable new(line, "0")
32
+ `(@~_ = ~_):
33
+ attrs << x left
24
34
 
25
- macro($(x: String)):
26
- Atomy::AST::GlobalVariable new(line, x value)
35
+ tmps = attrs collect [`@~name]: Atomy Grammar AST Unquote new(name)
27
36
 
28
- macro($(x: Constant)):
29
- Atomy::AST::GlobalVariable new(x line, x identifier)
37
+ pat = name
38
+ attrs zip(tmps) [attr, tmp]:
39
+ &pat = `(~pat & with(~attr, ~tmp))
30
40
 
31
- macro($(x: Variable)):
32
- Atomy::AST::GlobalVariable new(x line, x name)
41
+ cons = `((~name)(~*tmps))
33
42
 
34
- macro(#(x: Constant)):
35
- Atomy::AST::Particle new(x line, x identifier)
43
+ pat-def =
44
+ `(pattern(~cons):
45
+ pattern(~make-quasiquote(pat)))
36
46
 
37
- macro(#(x: Variable)):
38
- Atomy::AST::Particle new(x line, x name)
47
+ parent = (root || 'Object)
39
48
 
40
- macro(#(x: String)):
41
- Atomy::AST::Particle new(x line, x value)
49
+ `(do:
50
+ ~parent class(~name):
51
+ attr-accessor(
52
+ ~*(attrs collect [a]:
53
+ `.~(a node)))
42
54
 
43
- macro(#(x: Primitive)):
44
- Atomy::AST::Particle new(x line, x value to-s)
55
+ def(initialize(~*as)): nil
45
56
 
46
- macro(a .. b): `(Range new(~a, ~b))
47
- macro(a ... b): `(Range new(~a, ~b, true))
57
+ def(inspect):
58
+ result = (~(Atomy Code StringLiteral new(name text to-s)) + "(")
48
59
 
49
- macro(x for(e) in(c)): `(~c collect [~e]: ~x)
60
+ args = []
61
+ recursed = //Thread detect-recursion(self):
62
+ [~*attrs] each [v]:
63
+ args << v inspect
50
64
 
51
- macro(x for(e) in(c) if(t)):
52
- names [tmp]:
53
- `([] tap [~tmp]:
54
- ~c each [~e]:
55
- when(~t):
56
- ~tmp << ~x)
65
+ when(recursed):
66
+ args << "..."
67
+
68
+ result << args join(", ")
69
+ result << ")"
70
+
71
+ Rubinius Type infect(result, self)
72
+
73
+ result
74
+
75
+ ~pat-def
76
+
77
+ ~*(cs collect [c]: define-class(name, c)))
78
+
79
+ macro(data: ~*children):
80
+ `(Object data: ~*children)
81
+
82
+ macro(data(~x)):
83
+ `(Object data(~x) {})
84
+
85
+ macro(~root data(~x)):
86
+ `(~root data(~x) {})
87
+
88
+ macro(data(~parent): ~*children):
89
+ `(Object data(~parent): ~*children)
90
+
91
+ macro(~root data: ~*children):
92
+ `(do:
93
+ ~*(children collect [c]:
94
+ define-class(root, c))
95
+ nil)
96
+
97
+ macro(~root data(~parent): ~*children):
98
+ `(do:
99
+ ~(define-class(root, `(~(parent): ~*children)))
100
+ nil)
data/kernel/define.ay CHANGED
@@ -1,93 +1,84 @@
1
- macro(do(&b)): b body
2
-
3
- macro(x = y):
4
- Atomy::AST::Assign new(x line, x, y)
5
-
6
- macro(x =! y):
7
- Atomy::AST::Set new(x line, x, y)
8
-
9
- macro(x := y):
10
- where = Atomy::Namespace define-target
11
- Atomy::Namespace register(x namespace-symbol, where)
12
-
13
- Atomy::CodeLoader when-load <<
14
- [`(Atomy::Namespace register(~(x namespace-symbol), ~where)), true]
15
-
16
- Atomy::AST::Define new(x line, x, y, where)
17
-
18
- macro(define(x, &y)):
19
- `(~x := ~(y block-body))
20
-
21
- macro(x **= y): `(~x =! (~x ** ~y))
22
- macro(x *= y): `(~x =! (~x * ~y))
23
- macro(x <<= y): `(~x =! (~x << ~y))
24
- macro(x >>= y): `(~x =! (~x >> ~y))
25
- macro(x &&= y): `(~x =! (~x && ~y))
26
- macro(x &= y): `(~x =! (~x & ~y))
27
- macro(x ||= y): `(~x =! (~x || ~y))
28
- macro(x |= y): `(~x =! (~x | ~y))
29
- macro(x += y): `(~x =! (~x + ~y))
30
- macro(x -= y): `(~x =! (~x - ~y))
31
- macro(x /= y): `(~x =! (~x / ~y))
32
- macro(x ^= y): `(~x =! (~x ^ ~y))
33
- macro(x %= y): `(~x =! (~x % ~y))
34
-
35
- macro(x match(&b)):
36
- Atomy::AST::Match new(line, x, b)
37
-
38
- macro(class(`(<< ~obj), &body)):
39
- Atomy::AST::SingletonClass new(obj line, obj, body block-body)
40
-
41
- macro(class(`(~name < ~sup), &body)):
42
- Atomy::AST::Class new(name line, name, body block-body, sup)
43
-
44
- macro(class(name, &body)):
45
- Atomy::AST::Class new(name line, name, body block-body)
46
-
47
- macro(module(name, &body)):
48
- Atomy::AST::Module new(name line, name, body block-body)
49
-
50
- macro(evaluate-when('compile, 'run, 'load, &x)):
51
- x body evaluate
52
- Atomy::CodeLoader when-load << [x block-body, true]
53
- `(when(!(Atomy::CodeLoader compiled?) &&
54
- Atomy::CodeLoader reason == #run):
55
- ~*(x contents))
56
-
57
- macro(evaluate-when('run, 'compile, &x)):
58
- x body evaluate
59
- `(when(!(Atomy::CodeLoader compiled?) &&
60
- Atomy::CodeLoader reason == #run):
61
- ~*(x contents))
62
-
63
- macro(evaluate-when('compile, 'run, &x)):
64
- x body evaluate
65
- `(when(!(Atomy::CodeLoader compiled?) &&
66
- Atomy::CodeLoader reason == #run):
67
- ~*(x contents))
68
-
69
- macro(evaluate-when('compile, 'load, &x)):
70
- x body evaluate
71
- Atomy::CodeLoader when-load << [x block-body, true]
72
- 'nil
73
-
74
- macro(evaluate-when('compile, &x)):
75
- x body evaluate
76
- 'nil
77
-
78
- macro(evaluate-when('run, &x)):
79
- `(when(Atomy::CodeLoader reason == #run) { ~*(x contents) })
80
-
81
- macro(evaluate-when('load, &x)):
82
- Atomy::CodeLoader when-load << [x block-body, false]
83
- 'nil
84
-
85
- macro(for-macro(&b)):
86
- `(evaluate-when(compile, load):
87
- ~b block call-on-instance(Atomy::Macro::Environment singleton-class))
88
-
89
- macro(let-macro(*ms, &body)):
90
- macros = ms collect [`(~p = ~b)]:
91
- Atomy::AST::Macro new(p line, p, b)
92
-
93
- Atomy::AST::LetMacro new(line, body caller, macros)
1
+ use(require("core"))
2
+
3
+ require("atomy/message_structure")
4
+
5
+ -- function definition
6
+ macro(fn(~message): ~*body):
7
+ structure = Atomy MessageStructure new(message)
8
+
9
+ Atomy Code DefineFunction new(
10
+ structure name
11
+ `(do: ~*body)
12
+ structure receiver
13
+ structure arguments
14
+ structure default-arguments
15
+ structure splat-argument
16
+ structure post-arguments
17
+ structure proc-argument
18
+ )
19
+
20
+ macro(fn(~dummy-message)):
21
+ structure = Atomy MessageStructure new(dummy-message)
22
+ Atomy Code DefineFunction new(structure name)
23
+
24
+
25
+ -- method definition
26
+ macro(def(~message): ~*body):
27
+ structure = Atomy MessageStructure new(message)
28
+
29
+ Atomy Code DefineMethod new(
30
+ structure name
31
+ `(do: ~*body)
32
+ structure receiver
33
+ structure arguments
34
+ structure default-arguments
35
+ structure splat-argument
36
+ structure post-arguments
37
+ structure proc-argument
38
+ )
39
+
40
+ -- helper for adding a module to the constant scope
41
+ with-module = Class new:
42
+ def(bytecode(gen, _)):
43
+ gen push-self
44
+ gen add-scope
45
+ gen push-nil
46
+
47
+ current-module = Class new:
48
+ def(bytecode(gen, _)):
49
+ gen push-scope
50
+ gen send(.module, 0)
51
+
52
+ -- module/class opening
53
+ macro(~x open: ~*body):
54
+ `(~x module-eval:
55
+ ~(with-module new)
56
+ ~*body)
57
+
58
+ -- anonymous class creation
59
+ macro(class: ~*body): `(Object class: ~*body)
60
+
61
+ macro(~parent class: ~*body):
62
+ `(//Class new(~parent) open:
63
+ ~*body
64
+ self)
65
+
66
+ -- named class creation
67
+ macro(class(~name): ~*body): `(Object class(~name): ~*body)
68
+
69
+ macro(~parent class(~name): ~*body):
70
+ `(//Class new(~parent, .~name, ~(current-module new)) open:
71
+ ~*body
72
+ self)
73
+
74
+ -- singleton class opening
75
+ macro(singleton: ~*body): `(self singleton: ~*body)
76
+
77
+ macro(~x singleton: ~*body):
78
+ `(~x singleton-class open: ~*body)
79
+
80
+ -- module creation
81
+ macro(module: ~*body):
82
+ `(//Module new open:
83
+ ~*body
84
+ self)
data/kernel/doc.ay CHANGED
@@ -1,453 +1,286 @@
1
- namespace(atomy)
2
-
3
- title"Pretty-Printing"
4
-
1
+ use(require("core"))
2
+ use(require("data"))
3
+ use(require("define"))
4
+ use(require("control-flow"))
5
+ use(require("array"))
6
+ use(require("patterns"))
7
+ condition = use(require("condition"))
8
+
9
+ -- combinatorial structures
5
10
  data(Doc):
6
11
  Empty
7
- Beside(@left, @right, @space?)
8
- Above(@above, @below, @overlap?)
9
- Nest(@body, @depth)
12
+ Cat(@x, @y)
13
+ Nest(@depth, @doc)
10
14
  Text(@value)
11
-
12
- section("Documents"):
13
- doc"
14
- A document is a structural collection of text. The following are the \
15
- various types of documents, usually created with various combinators \
16
- provided below.
17
- "
18
-
19
- doc"
20
- The empty document, with a height and width of \hl{0}.
21
- " spec { => "Class" } for: "Empty"
22
-
23
- doc"
24
- A document comprised of two documents positioned beside each other, with \
25
- an optional space in-between.
26
- " spec { => "Class" } for: "Beside(@left, @right, @space?)"
27
-
28
- doc"
29
- A document comprised of two documents positioned above and below each \
30
- other, optionally overlapping if possible.
31
- " spec { => "Class" } for: "Above(@above, @below, @overlap?)"
32
-
33
- doc"
34
- A document indented to a given depth.
35
- " spec { => "Class" } for: "Nest(@body, @depth)"
36
-
37
- doc"
38
- A document containing arbitrary text, with a height of \hl{1}.
39
- " spec { => "Class" } for: "Text(@value)"
40
-
41
- doc"
42
- Trivial emptiness check. \hl{true} for \hl{Empty}, \hl{false} for \
43
- everything else.
44
- " spec {
45
- => "Boolean"
46
- } for {
47
- "Doc empty?"
48
-
49
- Doc empty? := false
50
- Empty empty? := true
51
- } examples:
52
- "doc { empty } empty?"
53
- "doc { text(\"abc\") } empty?"
54
- "doc { text(\"\") } empty?"
55
-
56
-
57
- doc"
58
- Calculate the width of the document, in characters.
59
- " spec {
60
- => "Integer"
61
- } for {
62
- "Doc width"
63
-
64
- Empty width := 0
65
- Text width := @value size
66
- Beside width :=
67
- if(@space?)
68
- then: 1 + @left width + @right width
69
- else: @left width + @right width
70
- Nest width := (@body width + @depth)
71
- Above width := [@above width, @below width] max
72
- } examples:
73
- "doc { empty } width"
74
- "doc { text(\"abc\") } width"
75
-
76
-
77
- doc"
78
- Calculate the height of the document, in lines.
79
-
80
- Note that an empty document has a height of 0.
81
- " spec {
82
- => "Integer"
83
- } for {
84
- "Doc height"
85
-
86
- Empty height := 0
87
- Text height := 1
88
- Beside height := [@left height, @right height] max
89
- Above height :=
90
- if(@overlap? && @below kind-of?(Nest)
91
- && @below depth > @above width)
92
- then: @above height + @below height
93
- else: 1 + @above height + @below height
94
- Nest height := @body height
95
- } examples:
96
- "doc { empty } height"
97
- "doc { text(\"abc\") } height"
98
-
99
-
100
- doc"
101
- Render the document as a \hl{String}.
102
- " spec {
103
- => "String"
104
- } for {
105
- "Doc render"
106
-
107
- Text render := @value
108
- Empty render := ""
109
- Beside render :=
110
- if(@space?)
111
- then: @left render + " " + @right render
112
- else: @left render + @right render
113
- Above render := do:
114
- a = @above render
115
- b = @below render
116
-
117
- if(@overlap? && @below kind-of?(Nest) &&
118
- @below depth > @above width)
15
+ Raw(@value)
16
+ Line(@space?)
17
+ Union(@x-thunk, @y-thunk, @prefer? = false)
18
+ Column(@thunk) -- @thunk is (Integer -> Doc)
19
+ Nesting(@thunk) -- @thunk is (Integer -> Doc)
20
+
21
+ -- simple documents (used internally)
22
+ data(SDoc):
23
+ SEmpty
24
+ SText(@value, @rest-thunk)
25
+ SRaw(@value, @rest-thunk)
26
+ SLine(@space?, @indentation, @rest-thunk)
27
+
28
+ fn(inject-right(xs, i = _) &block):
29
+ xs reverse inject(i) [x, y]: block [y, x]
30
+
31
+ -- lazy values
32
+ def(Union x): @x ||= @x-thunk call
33
+ def(Union y): @y ||= @y-thunk call
34
+ def(SText rest): @rest ||= @rest-thunk call
35
+ def(SRaw rest): @rest ||= @rest-thunk call
36
+ def(SLine rest): @rest ||= @rest-thunk call
37
+
38
+ -- constructors
39
+ def(Empty <> y ): y
40
+ def(Doc <> Empty): self
41
+ def(Doc <> y ): Cat new(self, y)
42
+
43
+ def(Proc <|> y): Union new(self, y)
44
+ def(Doc <|> y): Union new({ self }, { y })
45
+
46
+ def(Proc <||> y): Union new(self, y, true)
47
+ def(Doc <||> y): Union new({ self }, { y }, true)
48
+
49
+ def(Doc nest(i)): Nest new(i, self)
50
+
51
+ -- flatten a document
52
+ def(Empty flatten): Self empty
53
+ def(Cat(x, y) flatten): x flatten <> y flatten
54
+ def(Nest(i, x) flatten): x flatten
55
+ def(Text(s) flatten): Self text(s)
56
+ def(Raw(s) flatten): Self raw(s)
57
+ def(Line(true) flatten): Self text(" ")
58
+ def(Line(false) flatten): Self empty
59
+ def(Union flatten): x flatten
60
+ def(Column(f) flatten): Self column [x]: f [x] flatten
61
+ def(Nesting(f) flatten): Self nesting [x]: f [x] flatten
62
+
63
+ -- hanging indentation
64
+ def(Doc hang(i)): Self align(nest(i))
65
+
66
+ -- full indentation
67
+ def(Doc indent(i)): (Self text(" " * i) <> self) hang(i)
68
+
69
+ -- p punctuate([d1, d2, ..., dn]) => [d1 <> p, d2 <> p, ..., dn]
70
+ def(Doc punctuate([]) ): []
71
+ def(Doc punctuate([d]) ): [d]
72
+ def(Doc punctuate(d . ds)): [d <> self] + punctuate(ds)
73
+
74
+ -- sep + punctuate
75
+ def(Doc separate(ds)): Self align(Self sep(Self punctuate(ds)))
76
+
77
+ -- concatenate two documents, with a space in-between
78
+ def(Empty <+> y ): y
79
+ def(Doc <+> Empty): self
80
+ def(Doc <+> y ): (self <> Self space) <> y
81
+
82
+ -- concatenates two documents, with a `softline' in between
83
+ def(Empty </> y ): y
84
+ def(Doc </> Empty): self
85
+ def(Doc </> y ): (self <> Self softline) <> y
86
+
87
+ -- concatenates two documents, with a `suggestline' in between
88
+ def(Empty <\> y ): y
89
+ def(Doc <\> Empty): self
90
+ def(Doc <\> y ): (self <> Self suggestline) <> y
91
+
92
+ -- concatenates two documents, with a `softbreak' in between
93
+ def(Empty <//> y ): y
94
+ def(Doc <//> Empty): self
95
+ def(Doc <//> y ): (self <> Self softbreak) <> y
96
+
97
+ -- concatenates two documents, with a `suggestbreak' in between
98
+ def(Empty <\\> y ): y
99
+ def(Doc <\\> Empty): self
100
+ def(Doc <\\> y ): (self <> Self suggestbreak) <> y
101
+
102
+ -- concatenates two documents, with a `line' in between
103
+ def(Empty <$> y ): y
104
+ def(Doc <$> Empty): self
105
+ def(Doc <$> y ): (self <> Self line) <> y
106
+
107
+ -- concatenates two documents, with a `linebreak' in between
108
+ def(Empty <$$> y ): y
109
+ def(Doc <$$> Empty): self
110
+ def(Doc <$$> y ): (self <> Self linebreak) <> y
111
+
112
+ -- predeclare so pick-union can use it
113
+ fn(fit)
114
+
115
+ -- chose the side of a Union that best fits the given width
116
+ fn(pick-union(w, k, i, ds, x, y, prefer?)):
117
+ flattened = fit(w, k, [i, x] . ds, prefer?)
118
+ if(flattened fits?(w - k))
119
+ then: flattened
120
+ else:
121
+ condition signal(.no-fit)
122
+ fit(w, k, [i, y] . ds, prefer?)
123
+
124
+ -- helper for `best' by linearizing documents
125
+ fn(fit(_, _, [], pref = false)): SEmpty new
126
+ fn(fit(w, k, [i, d] . ds, pref = false)):
127
+ d match:
128
+ Empty : fit(w, k, ds, pref)
129
+ Cat(x, y) : fit(w, k, [[i, x], [i, y]] + ds, pref)
130
+ Nest(j, x) : fit(w, k, [i + j, x] . ds, pref)
131
+ Text(s) : SText new(s, { fit(w, k + s size, ds, pref) })
132
+ Raw(s) : SRaw new(s, { fit(w, k, ds, pref) })
133
+ Line(s) : SLine new(s, i, { fit(w, i, ds, false) })
134
+ Column(f) : fit(w, k, [i, f[k]] . ds, pref)
135
+ Nesting(f) : fit(w, k, [i, f[i]] . ds, pref)
136
+ Union:
137
+ if(d prefer? && !pref)
119
138
  then:
120
- b slice!(0, a size)
121
- a + b
122
- else:
123
- a + "\n" + b
124
- Nest render := do:
125
- b = @body render
126
- x = ""
127
- b each-line [l]:
128
- x << " " * @depth + l
129
- x
130
- } examples:
131
- "doc { empty } render"
132
- "doc { text(\"abc\") } render"
133
- "'(1 + 1) pretty render"
134
-
135
-
136
- section("Constructing"):
137
- doc"
138
- Position one document beside another, separated by a space, unless either\
139
- side is empty.
140
- " spec {
141
- => "Doc"
142
- } for {
143
- "(left: Doc) <+> (right: Doc)"
144
-
145
- (l: Doc) <+> (r: Doc) := Beside new(l, r, true)
146
- (d: Doc) <+> Empty := d
147
- Empty <+> (d: Doc) := d
148
- (l: Doc) <+> (n: Nest) := (l <+> n body)
149
- (l: Doc) <+> (a: Above) := do:
150
- first = l <+> a above
151
- rest = a below nest(l width + 1)
152
- Above new(first, rest, a overlap?)
153
- } examples:
154
- "doc { text(\"x\") <+> value(42) }"
155
- "doc { empty <+> value(42) }"
156
-
157
-
158
- doc"
159
- Position one document beside another unless either side is empty.
160
- " spec {
161
- => "Doc"
162
- } for {
163
- "(left: Doc) <> (right: Doc)"
164
-
165
- (l: Doc) <> (r: Doc) := Beside new(l, r, false)
166
- (d: Doc) <> Empty := d
167
- Empty <> (d: Doc) := d
168
- (l: Doc) <> (n: Nest) := l <> n body
169
- (l: Doc) <> (a: Above) := do:
170
- first = l <> a above
171
- rest = a below nest(l width)
172
- Above new(first, rest, a overlap?)
173
- } examples:
174
- "doc { text(\"x\") <> value(42) }"
175
- "doc { empty <> value(42) }"
176
-
177
-
178
- doc"
179
- Position one document above another, unless either are empty.
180
-
181
- If the last line of the first argument stops at least one position before\
182
- the first line of the second begins, these two lines are overlapped.
183
- " spec {
184
- => "Doc"
185
- } for {
186
- "(above: Doc) // (below: Doc)"
187
-
188
- (a: Doc) // (b: Doc) := Above new(a, b, true)
189
- (a: Doc) // Empty := a
190
- Empty // (b: Doc) := b
191
- } examples:
192
- "doc { text(\"hi\") // text(\"there\") nest(1) }"
193
- "doc { text(\"hi\") // text(\"there\") nest(5) }"
194
-
195
-
196
- doc"
197
- Position one document above another, without overlapping, unless either \
198
- are empty.
199
- " spec {
200
- => "Doc"
201
- } for {
202
- "(above: Doc) /+/ (below: Doc)"
203
-
204
- (a: Doc) /+/ (b: Doc) := Above new(a, b, false)
205
- (a: Doc) /+/ Empty := a
206
- Empty /+/ (b: Doc) := b
207
- } examples:
208
- "doc { text(\"hi\") /+/ text(\"there\") nest(1) }"
209
- "doc { text(\"hi\") /+/ text(\"there\") nest(5) }"
210
-
211
-
212
- doc"
213
- Indent the document to the given \hl{depth}. For \hl{Nest}, this just \
214
- increases its indentation level.
215
- " spec {
216
- "depth is-a?(Integer)"
217
- => "Doc"
218
- } for {
219
- "Doc nest(depth)"
220
-
221
- Nest nest(i) := Nest new(@body, @depth + i)
222
- (d: Doc) nest(i) := Nest new(d, i)
223
- } examples:
224
- "doc { text(\"hi\") nest(5) }"
225
- "doc { text(\"hi\") nest(5) nest(6) }"
226
-
227
-
228
- doc"
229
- Intersperse \hl{delimiter} document through \hl{documents}.
230
- " spec {
231
- "documents all?(&#is-a?(Doc))"
232
- => "List"
233
- } for {
234
- "(delimiter: Doc) punctuate(documents: List)"
235
-
236
- Doc punctuate([]) := []
237
- Doc punctuate([d]) := [d]
238
- (delim: Doc) punctuate(d . ds) :=
239
- (delim punctuate(ds)) unshift(d <> delim)
240
- } examples:
241
- "doc { semi punctuate([value(1), value(2), value(3)]) }"
242
- "doc { hsep(semi punctuate([value(1), value(2), value(3)])) }"
243
-
244
-
245
- section("Helpers"):
246
- doc"
247
- Shortcut for \hl{Doc onto(&body)}. This provides the various helper \
248
- methods below, which are also available in the \hl{Pretty} module.
249
- " for:
250
- "doc(&body)"
251
- macro(doc(&body)):
252
- `(Doc onto(&~body))
253
-
254
- module(Pretty):
255
- section("Shortcuts"):
256
- doc"
257
- These shortcuts are primarily for quickly creating \hl{Text} \
258
- documents, especially for things commonly found in syntax.
259
- "
260
-
261
- doc"
262
- Create a \hl{Text} document with the given contents.
263
- " spec {
264
- "s is-a?(String)"
265
- => "Doc"
266
- } for:
267
- "text(s)"
268
- text(s) := Text new(s)
269
-
270
- doc"
271
- Create a \hl{Text} document with \hl{x inspect}.
272
- " spec {
273
- => "Doc"
274
- } for:
275
- "value(x)"
276
- value(x) := Text new(x inspect)
277
-
278
- doc"
279
- Shortcut for \hl{text(\";\")}.
280
- " spec {
281
- => "Doc"
282
- } for:
283
- "semi"
284
- semi := text(";")
285
-
286
- doc"
287
- Shortcut for \hl{text(\",\")}.
288
- " spec {
289
- => "Doc"
290
- } for:
291
- "comma"
292
- comma := text(",")
293
-
294
- doc"
295
- Shortcut for \hl{text(\":\")}.
296
- " spec {
297
- => "Doc"
298
- } for:
299
- "colon"
300
- colon := text(":")
301
-
302
- doc"
303
- Shortcut for \hl{text(\" \")}.
304
- " spec {
305
- => "Doc"
306
- } for:
307
- "space"
308
- space := text(" ")
309
-
310
- doc"
311
- Shortcut for \hl{text(\"=\")}.
312
- " spec {
313
- => "Doc"
314
- } for:
315
- "equals"
316
- equals := text("=")
317
-
318
- doc"
319
- Shortcut for \hl{text(\"(\")}.
320
- " spec {
321
- => "Doc"
322
- } for:
323
- "lparen"
324
- lparen := text("(")
325
-
326
- doc"
327
- Shortcut for \hl{text(\")\")}.
328
- " spec {
329
- => "Doc"
330
- } for:
331
- "rparen"
332
- rparen := text(")")
333
-
334
- doc"
335
- Shortcut for \hl{text(\"[\")}.
336
- " spec {
337
- => "Doc"
338
- } for:
339
- "lbrack"
340
- lbrack := text("[")
341
-
342
- doc"
343
- Shortcut for \hl{text(\"]\")}.
344
- " spec {
345
- => "Doc"
346
- } for:
347
- "rbrack"
348
- rbrack := text("]")
349
-
350
- doc"
351
- Shortcut for \hl{text(\"\{\")}.
352
- " spec {
353
- => "Doc"
354
- } for:
355
- "lbrace"
356
- lbrace := text("{")
357
-
358
- doc"
359
- Shortcut for \hl{text(\"\}\")}.
360
- " spec {
361
- => "Doc"
362
- } for:
363
- "rbrace"
364
- rbrace := text("}")
365
-
366
- section("Wrapping"):
367
- doc"
368
- More shortcuts for common cases in pretty-printing, dealing with \
369
- wrapping a document in delimiters.
370
- "
371
-
372
- doc"
373
- Wrap a document in parentheses.
374
- " spec {
375
- => "Doc"
376
- } for:
377
- "parens(d)"
378
- parens(d) := lparen <> d <> rparen
379
-
380
- doc"
381
- Wrap a document in brackets (\code{[]}).
382
- " spec {
383
- => "Doc"
384
- } for:
385
- "brackets(d: Doc)"
386
- brackets(d) := lbrack <> d <> rbrack
387
-
388
- doc"
389
- Wrap a document in braces (\code{\{\}}).
390
- " spec {
391
- => "Doc"
392
- } for:
393
- "braces(d: Doc)"
394
- braces(d) := lbrace <> d <> rbrace
395
-
396
- doc"
397
- Wrap a document in single-quotes.
398
- " spec {
399
- => "Doc"
400
- } for:
401
- "quotes(d: Doc)"
402
- quotes(d) := text("'") <> d <> text("'")
403
-
404
- doc"
405
- Wrap a document in double-quotes.
406
- " spec {
407
- => "Doc"
408
- } for:
409
- "double-quotes(d: Doc)"
410
- double-quotes(d) := text("\"") <> d <> text("\"")
411
-
412
- section("Combining"):
413
- doc"
414
- Helpers for creating a single document from many.
415
- "
416
-
417
- doc"
418
- An empty document. The identity for \hl{<>}, \hl{<+>}, \hl{//}, \
419
- \hl{/+/}, and anywhere in the following methods.
420
- " spec {
421
- => "Doc"
422
- } for:
423
- "empty"
424
- empty := Empty new
425
-
426
- doc"
427
- Reduce a list of documents with \hl{<>}.
428
- " spec {
429
- "ds all?(&#is-a?(Doc))"
430
- => "Doc"
431
- } for:
432
- "hcat(ds)"
433
- hcat(ds) := ds inject(empty) [a, b]: a <> b
434
-
435
- doc"
436
- Reduce a list of documents with \hl{<+>}.
437
- " spec {
438
- "ds all?(&#is-a?(Doc))"
439
- => "Doc"
440
- } for:
441
- "hsep(ds)"
442
- hsep(ds) := ds inject(empty) [a, b]: a <+> b
443
-
444
- doc"
445
- Reduce a list of documents with \hl{//}.
446
- " spec {
447
- "ds all?(&#is-a?(Doc))"
448
- => "Doc"
449
- } for:
450
- "vcat(ds)"
451
- vcat(ds) := ds inject(empty) [a, b]: a // b
452
-
453
- Doc extend(Pretty)
139
+ with-restarts(use-fit: fit(w, k, [i, d y] . ds, false)) {
140
+ pick-union(w, k, i, ds, d x, d y, true)
141
+ } bind:
142
+ .no-fit: condition restart(.use-fit)
143
+ else: pick-union(w, k, i, ds, d x, d y, pref)
144
+
145
+ -- find the best configuration to fit document X in width w
146
+ def(Doc best(w, k)): fit(w, k, [[0, self]])
147
+
148
+ -- test that a document can fit in a given width
149
+ def(SText fits?(w)): (w >= 0) && rest fits?(w - @value size)
150
+ def(SRaw fits?(w)): (w >= 0) && rest fits?(w)
151
+ def(SDoc fits?(w)): w >= 0
152
+
153
+ -- rendering to a string
154
+ def(SEmpty layout): ""
155
+ def(SText layout): @value + rest layout
156
+ def(SRaw layout): @value + rest layout
157
+ def(SLine layout): ("\n" + (" " * @indentation)) + rest layout
158
+
159
+ -- rendering to a string
160
+ def(Cat(x, y) layout): x layout + y layout
161
+ def(Empty layout): ""
162
+ def(Text(s) layout): s
163
+ def(Raw(s) layout): s
164
+ def(Line layout): "\n"
165
+ def(Union layout): y layout
166
+ def(Nest(i, d) layout):
167
+ d match:
168
+ Cat(x, y) : x nest(i) layout + y nest(i) layout
169
+ Empty : ""
170
+ Text(s) : s
171
+ Raw(s) : s
172
+ Line : "\n" + (" " * i)
173
+ Nest(e, x) : Nest new(i + e, x) layout
174
+ Union : Nest new(i, d y) layout
175
+
176
+ -- trivial emptiness check
177
+ def(Empty empty?): true
178
+ def(Doc empty?): false
179
+
180
+ -- pretty-printing with a maximum width
181
+ def(Doc render(width = 70)): best(width, 0) layout
182
+
183
+ -- trivial constructors
184
+ def(empty): Empty new
185
+ def(text(s)): Text new(s to-s)
186
+ def(raw(s)): Raw new(s to-s)
187
+ def(line): Line new(true)
188
+ def(linebreak): Line new(false)
189
+ def(column &f): Column new(f)
190
+ def(nesting &f): Nesting new(f)
191
+ def(group(x)): { x flatten } <|> { x }
192
+ def(preferred-group(x)): { x flatten } <||> { x }
193
+ def(softline): group(line)
194
+ def(softbreak): group(linebreak)
195
+ def(suggestline): preferred-group(line)
196
+ def(suggestbreak): preferred-group(linebreak)
197
+
198
+ -- render x with the nesting level set to the current column
199
+ def(align(x)):
200
+ column [k]:
201
+ nesting [i]:
202
+ x nest(k - i)
203
+
204
+ -- wrap `x' in `left' and `right'
205
+ def(enclose(left, right, x)): (left <> x) <> right
206
+
207
+ -- punctuate `xs' with `delim', separating with a space, and wrapping with
208
+ -- `left` and `right`
209
+ def(enclose-sep(left, right, delim, xs)):
210
+ enclose(left, right, align(sep(delim punctuate(xs))))
211
+
212
+ -- punctuate `xs' with `delim', and wrapping with `left` and `right`
213
+ def(enclose-cat(left, right, delim, xs)):
214
+ enclose(left, right, align(cat(delim punctuate(xs))))
215
+
216
+ -- separating lists
217
+ def(hsep(xs)): inject-right(xs, empty) [x, y]: x <+> y
218
+ def(vsep(xs)): inject-right(xs, empty) [x, y]: x <$> y
219
+ def(sep(xs)): group(vsep(xs))
220
+ def(fill-sep(xs)): inject-right(xs, empty) [x, y]: x </> y
221
+ def(suggest-sep(xs)): inject-right(xs, empty) [x, y]: x <\> y
222
+
223
+ -- concatenating lists
224
+ def(hcat(xs)): inject-right(xs, empty) [x, y]: x <> y
225
+ def(vcat(xs)): inject-right(xs, empty) [x, y]: x <$$> y
226
+ def(cat(xs)): group(vcat(xs))
227
+ def(fill-cat(xs)): inject-right(xs, empty) [x, y]: x <//> y
228
+ def(suggest-cat(xs)): inject-right(xs, empty) [x, y]: x <\\> y
229
+
230
+ -- fill a document with whitespace to width `i'
231
+ def(fill(i, d)):
232
+ width(d) [w]:
233
+ if(w >= i)
234
+ then: empty
235
+ else: text(" " * (i - w))
236
+
237
+ -- fill a document with whitespace to width `i', breaking onto a new line if
238
+ -- the document gets too long
239
+ def(fill-break(i, d)):
240
+ width(d) [w]:
241
+ if(w > i)
242
+ then: linebreak nest(i)
243
+ else: text(" " * (i - w))
244
+
245
+ -- call `f' with the width of the passed document, and render the result
246
+ -- after it
247
+ def(width(d) &f):
248
+ column [c1]: d <> column [c2]: f [c2 - c1]
249
+
250
+ -- helper documents
251
+ def(lparen): text("(")
252
+ def(rparen): text(")")
253
+ def(langle): text("<")
254
+ def(rangle): text(">")
255
+ def(lbrace): text("{")
256
+ def(rbrace): text("}")
257
+ def(lbracket): text("[")
258
+ def(rbracket): text("]")
259
+ def(squote): text("'")
260
+ def(dquote): text("\"")
261
+ def(semi): text(";")
262
+ def(colon): text(":")
263
+ def(comma): text(",")
264
+ def(space): text(" ")
265
+ def(dot): text(".")
266
+ def(backslash): text("\\")
267
+ def(equals): text("=")
268
+
269
+ -- helper for creating text from a string that may contain newlines
270
+ def(string(s)): hcat(line punctuate(s split("\n") collect [x]: text(x)))
271
+
272
+ -- wrappers
273
+ def(parens(x)): (lparen <> x) <> rparen
274
+ def(angles(x)): (langle <> x) <> rangle
275
+ def(braces(x)): (lbrace <> x) <> rbrace
276
+ def(brackets(x)): (lbracket <> x) <> rbracket
277
+ def(squotes(x)): (squote <> x) <> squote
278
+ def(dquotes(x)): (dquote <> x) <> dquote
279
+
280
+ -- helpers for common syntax
281
+ def(list(xs)):
282
+ enclose(lbracket, rbracket, align(fill-sep(comma punctuate(xs))))
283
+ def(tupled(xs)):
284
+ enclose(lparen, rparen, align(fill-sep(comma punctuate(xs))))
285
+ def(semi-braces(xs)):
286
+ enclose-sep(lbrace, rbrace, semi, xs)