atomy 0.1.1 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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)