atomy 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +30 -0
- data/README.md +1 -0
- data/bin/atomy +134 -0
- data/kernel/block.ay +30 -0
- data/kernel/boot.ay +10 -0
- data/kernel/comparison.ay +61 -0
- data/kernel/concurrency.ay +84 -0
- data/kernel/condition.ay +277 -0
- data/kernel/control-flow.ay +222 -0
- data/kernel/cosmetics.ay +3 -0
- data/kernel/data-delta.ay +105 -0
- data/kernel/data.ay +56 -0
- data/kernel/define.ay +93 -0
- data/kernel/doc.ay +453 -0
- data/kernel/documentation.ay +135 -0
- data/kernel/dynamic.ay +42 -0
- data/kernel/errors.ay +6 -0
- data/kernel/format.ay +13 -0
- data/kernel/format/data.ay +89 -0
- data/kernel/format/formatter.ay +345 -0
- data/kernel/format/parser.ay +13 -0
- data/kernel/hashes.ay +39 -0
- data/kernel/io.ay +244 -0
- data/kernel/namespaces.ay +63 -0
- data/kernel/node.ay +48 -0
- data/kernel/operators.ay +28 -0
- data/kernel/particles.ay +53 -0
- data/kernel/patterns.ay +256 -0
- data/kernel/precision.ay +148 -0
- data/kernel/pretty.ay +283 -0
- data/kernel/repl.ay +140 -0
- data/kernel/therie.ay +204 -0
- data/lib/ast/binary_send.rb +44 -0
- data/lib/ast/block.rb +268 -0
- data/lib/ast/constant.rb +88 -0
- data/lib/ast/internal/assign.rb +19 -0
- data/lib/ast/internal/block_pass.rb +21 -0
- data/lib/ast/internal/catch.rb +247 -0
- data/lib/ast/internal/class.rb +30 -0
- data/lib/ast/internal/class_variable.rb +23 -0
- data/lib/ast/internal/define.rb +174 -0
- data/lib/ast/internal/ensure.rb +135 -0
- data/lib/ast/internal/file.rb +14 -0
- data/lib/ast/internal/global_variable.rb +20 -0
- data/lib/ast/internal/if_then_else.rb +24 -0
- data/lib/ast/internal/instance_variable.rb +17 -0
- data/lib/ast/internal/let_macro.rb +35 -0
- data/lib/ast/internal/macro_quote.rb +23 -0
- data/lib/ast/internal/match.rb +53 -0
- data/lib/ast/internal/module.rb +30 -0
- data/lib/ast/internal/pattern.rb +17 -0
- data/lib/ast/internal/return.rb +29 -0
- data/lib/ast/internal/set.rb +19 -0
- data/lib/ast/internal/singleton_class.rb +18 -0
- data/lib/ast/internal/splat.rb +14 -0
- data/lib/ast/internal/when.rb +24 -0
- data/lib/ast/list.rb +25 -0
- data/lib/ast/macro.rb +37 -0
- data/lib/ast/node.rb +599 -0
- data/lib/ast/operator.rb +21 -0
- data/lib/ast/particle.rb +13 -0
- data/lib/ast/primitive.rb +20 -0
- data/lib/ast/quasi_quote.rb +20 -0
- data/lib/ast/quote.rb +13 -0
- data/lib/ast/send.rb +104 -0
- data/lib/ast/splice.rb +32 -0
- data/lib/ast/string.rb +23 -0
- data/lib/ast/unary.rb +44 -0
- data/lib/ast/unquote.rb +45 -0
- data/lib/ast/variable.rb +64 -0
- data/lib/atomy.kpeg.rb +3995 -0
- data/lib/code_loader.rb +137 -0
- data/lib/compiler/compiler.rb +155 -0
- data/lib/compiler/stages.rb +81 -0
- data/lib/formatter.kpeg.rb +1394 -0
- data/lib/macros.rb +317 -0
- data/lib/method.rb +261 -0
- data/lib/namespace.rb +236 -0
- data/lib/parser.rb +28 -0
- data/lib/patterns.rb +276 -0
- data/lib/patterns/any.rb +21 -0
- data/lib/patterns/attribute.rb +59 -0
- data/lib/patterns/block_pass.rb +54 -0
- data/lib/patterns/constant.rb +33 -0
- data/lib/patterns/default.rb +44 -0
- data/lib/patterns/head_tail.rb +63 -0
- data/lib/patterns/list.rb +77 -0
- data/lib/patterns/match.rb +45 -0
- data/lib/patterns/named.rb +55 -0
- data/lib/patterns/named_class.rb +46 -0
- data/lib/patterns/named_global.rb +46 -0
- data/lib/patterns/named_instance.rb +46 -0
- data/lib/patterns/particle.rb +29 -0
- data/lib/patterns/quasi_quote.rb +184 -0
- data/lib/patterns/quote.rb +33 -0
- data/lib/patterns/singleton_class.rb +31 -0
- data/lib/patterns/splat.rb +57 -0
- data/lib/util.rb +37 -0
- metadata +164 -0
@@ -0,0 +1,135 @@
|
|
1
|
+
namespace(atomy/documentation)
|
2
|
+
|
3
|
+
in-namespace(atomy):
|
4
|
+
symbols(pretty, show)
|
5
|
+
|
6
|
+
for-macro:
|
7
|
+
escape(x) :=
|
8
|
+
x gsub(r"[\{\}\\]", raw"\\\0")
|
9
|
+
|
10
|
+
indent(x, n = 1) :=
|
11
|
+
x split("\n") collect [x] { (" " * n * 2) + x } join("\n")
|
12
|
+
|
13
|
+
str-expr(e) :=
|
14
|
+
escape $:
|
15
|
+
e match:
|
16
|
+
String -> e
|
17
|
+
Atomy::AST::String -> e value
|
18
|
+
_ -> e show
|
19
|
+
|
20
|
+
to-thumb(x: Atomy::AST::Macro) :=
|
21
|
+
indent(escape(x pattern show))
|
22
|
+
to-thumb(`(~meth := ~_)) :=
|
23
|
+
indent(escape(meth show))
|
24
|
+
to-thumb(d: `(data(Object))) :=
|
25
|
+
let(atomy/pretty/multiline? = true):
|
26
|
+
indent(escape(d block body show))
|
27
|
+
to-thumb(d: `(data(~r))) :=
|
28
|
+
let(atomy/pretty/multiline? = true):
|
29
|
+
indent(escape(`(~r ~(d block)) show))
|
30
|
+
to-thumb(`(dynamic(~n, ~r))) :=
|
31
|
+
indent(`^~n show + "\n> " + r show)
|
32
|
+
to-thumb(`(class(~n))) :=
|
33
|
+
indent(n show + "\n> Class")
|
34
|
+
to-thumb(`(module(~n))) :=
|
35
|
+
indent(n show + "\n> Module")
|
36
|
+
to-thumb(x) := str-expr(x)
|
37
|
+
|
38
|
+
to-spec([]) := ""
|
39
|
+
to-spec(b) :=
|
40
|
+
"\n" + indent $:
|
41
|
+
b collect [e] {
|
42
|
+
e match:
|
43
|
+
`(=> ~x) ->
|
44
|
+
"> " + str-expr(x)
|
45
|
+
|
46
|
+
_ ->
|
47
|
+
"| " + str-expr(e)
|
48
|
+
} join("\n")
|
49
|
+
|
50
|
+
to-examples(b) :=
|
51
|
+
b collect [e] {
|
52
|
+
" " + str-expr(e)
|
53
|
+
} join("\n")
|
54
|
+
|
55
|
+
export-to(atomy):
|
56
|
+
-- TODO: shoudln't need to export this; not being resolved in #section
|
57
|
+
macro(for-docs(&body)):
|
58
|
+
`(when(^documentation):
|
59
|
+
^documentation << ~(body block-body))
|
60
|
+
|
61
|
+
macro-quoter(doc) [c]:
|
62
|
+
for-docs:
|
63
|
+
c + "\n\n"
|
64
|
+
|
65
|
+
c
|
66
|
+
|
67
|
+
macro-quoter(title) [c]:
|
68
|
+
for-docs:
|
69
|
+
"\\title{" + escape(c) + "}\n\n"
|
70
|
+
|
71
|
+
c
|
72
|
+
|
73
|
+
macro(section(name, &body)):
|
74
|
+
`(do:
|
75
|
+
evaluate-when(compile):
|
76
|
+
for-docs: "\\section{" + ~name + "}{\n\\style{Atomy}\n\n"
|
77
|
+
|
78
|
+
res = ~(body caller)
|
79
|
+
|
80
|
+
evaluate-when(compile):
|
81
|
+
for-docs: "}"
|
82
|
+
|
83
|
+
res)
|
84
|
+
|
85
|
+
macro(docs spec(&s) for(&code)):
|
86
|
+
for-docs:
|
87
|
+
[ "\\define{"
|
88
|
+
"\n" + to-thumb(code contents first)
|
89
|
+
to-spec(s contents)
|
90
|
+
"\n}{"
|
91
|
+
docs contents
|
92
|
+
"}\n\n"
|
93
|
+
] join
|
94
|
+
|
95
|
+
code block-body
|
96
|
+
|
97
|
+
macro(docs for(&code)):
|
98
|
+
for-docs:
|
99
|
+
[ "\\define{"
|
100
|
+
"\n" + to-thumb(code contents first)
|
101
|
+
"\n}{"
|
102
|
+
docs contents
|
103
|
+
"}\n\n"
|
104
|
+
] join
|
105
|
+
|
106
|
+
code block-body
|
107
|
+
|
108
|
+
macro(docs spec(&s) for(&code) examples(&es)):
|
109
|
+
for-docs:
|
110
|
+
[ "\\define{"
|
111
|
+
"\n" + to-thumb(code contents first)
|
112
|
+
to-spec(s contents)
|
113
|
+
"\n}{"
|
114
|
+
docs contents
|
115
|
+
"\n \\examples{"
|
116
|
+
"\n" + to-examples(es contents)
|
117
|
+
"\n }"
|
118
|
+
"\n}\n\n"
|
119
|
+
] join
|
120
|
+
|
121
|
+
code block-body
|
122
|
+
|
123
|
+
macro(docs for(&code) examples(&es)):
|
124
|
+
for-docs:
|
125
|
+
[ "\\define{"
|
126
|
+
"\n" + to-thumb(code contents first)
|
127
|
+
"\n}{"
|
128
|
+
docs contents
|
129
|
+
"\n \\examples{"
|
130
|
+
"\n" + to-examples(es contents)
|
131
|
+
"\n }"
|
132
|
+
"\n}\n\n"
|
133
|
+
] join
|
134
|
+
|
135
|
+
code block-body
|
data/kernel/dynamic.ay
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
Atomy::DYNAMIC_ROOTS = Hash new
|
2
|
+
|
3
|
+
for-macro:
|
4
|
+
key(n) :=
|
5
|
+
Atomy::AST::Variable new $:
|
6
|
+
n line
|
7
|
+
"atomy_" + n resolve message-name
|
8
|
+
|
9
|
+
dynvar(x) :=
|
10
|
+
`(Thread current [#~key(x)] || Atomy::DYNAMIC_ROOTS [#~x])
|
11
|
+
|
12
|
+
set-dynvar(n, v) :=
|
13
|
+
`(Thread current [#~key(n)] = ~v)
|
14
|
+
|
15
|
+
macro(dynamic(n, root)):
|
16
|
+
where = Atomy::Namespace define-target
|
17
|
+
|
18
|
+
Atomy::Namespace register(n namespace-symbol, where)
|
19
|
+
|
20
|
+
Atomy::CodeLoader when-load <<
|
21
|
+
[`(Atomy::Namespace register(~(n namespace-symbol), ~where)), true]
|
22
|
+
|
23
|
+
`(Atomy::DYNAMIC_ROOTS [#~n] = ~root)
|
24
|
+
|
25
|
+
macro(^x): dynvar(x)
|
26
|
+
|
27
|
+
macro(let(*bindings, &body)):
|
28
|
+
tmps = names(bindings size)
|
29
|
+
save = []
|
30
|
+
set = []
|
31
|
+
restore = []
|
32
|
+
|
33
|
+
bindings zip(tmps) [[`(~n = ~v), tmp]]:
|
34
|
+
save << `(~tmp = ~(dynvar(n)))
|
35
|
+
set << set-dynvar(n, v)
|
36
|
+
restore << set-dynvar(n, tmp)
|
37
|
+
|
38
|
+
`(do:
|
39
|
+
~*save
|
40
|
+
{ ~*set
|
41
|
+
~(body caller)
|
42
|
+
} ensuring: ~*restore)
|
data/kernel/errors.ay
ADDED
data/kernel/format.ay
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
namespace(atomy/format)
|
2
|
+
|
3
|
+
base = File expand-path("../", _FILE)
|
4
|
+
|
5
|
+
import(base + "/format/data")
|
6
|
+
import(base + "/format/formatter")
|
7
|
+
import(base + "/format/parser")
|
8
|
+
|
9
|
+
symbols(parse)
|
10
|
+
|
11
|
+
export-to(atomy):
|
12
|
+
macro-quoter(f) [c]:
|
13
|
+
Atomy::Format::Parser parse(c)
|
@@ -0,0 +1,89 @@
|
|
1
|
+
namespace(atomy/format)
|
2
|
+
|
3
|
+
macro(ast(root, &nodes)):
|
4
|
+
parent = root || 'Atomy::AST::Node
|
5
|
+
cs = nodes contents map [e]:
|
6
|
+
e match:
|
7
|
+
Atomy::AST::Send -> do:
|
8
|
+
name = Atomy::AST::Constant new(0, e method-name)
|
9
|
+
|
10
|
+
children = []
|
11
|
+
attributes = []
|
12
|
+
|
13
|
+
e arguments each [a]:
|
14
|
+
a match:
|
15
|
+
`[@~name] ->
|
16
|
+
attributes << `[#~name]
|
17
|
+
|
18
|
+
`@~name ->
|
19
|
+
attributes << `#~name
|
20
|
+
|
21
|
+
_ ->
|
22
|
+
children << a
|
23
|
+
|
24
|
+
`(class(~name < ~parent):
|
25
|
+
children(~*children)
|
26
|
+
attributes(~*attributes)
|
27
|
+
generate)
|
28
|
+
|
29
|
+
_ ->
|
30
|
+
`(class(~e < ~parent):
|
31
|
+
generate)
|
32
|
+
|
33
|
+
when(root):
|
34
|
+
cs unshift(`(class(~root < Atomy::AST::Node) {}))
|
35
|
+
|
36
|
+
`(do: ~*cs)
|
37
|
+
|
38
|
+
module(Atomy::Format):
|
39
|
+
ast(Segment):
|
40
|
+
Chunk([#flags], @text)
|
41
|
+
String([#flags])
|
42
|
+
Decimal([#flags])
|
43
|
+
Hex([#flags])
|
44
|
+
Octal([#flags])
|
45
|
+
Binary([#flags])
|
46
|
+
Radix([#flags])
|
47
|
+
Float([#flags])
|
48
|
+
Exponent([#flags])
|
49
|
+
General([#flags])
|
50
|
+
Character([#flags])
|
51
|
+
Any([#flags])
|
52
|
+
Pluralize(#singular, [#flags], #plural?)
|
53
|
+
Lowercase(#content, [#flags])
|
54
|
+
Capitalize(#content, [#flags])
|
55
|
+
Uppercase(#content, [#flags])
|
56
|
+
Justify([#segments], [#flags])
|
57
|
+
Skip([#flags])
|
58
|
+
Indirection([#flags])
|
59
|
+
Iterate(#content, [#flags])
|
60
|
+
Break([#flags])
|
61
|
+
Conditional([#branches], [#flags], #default?)
|
62
|
+
|
63
|
+
ast(Flag):
|
64
|
+
Number(@value)
|
65
|
+
Symbol(@character)
|
66
|
+
ZeroPad
|
67
|
+
Precision(@value)
|
68
|
+
|
69
|
+
ast:
|
70
|
+
Formatter([#segments])
|
71
|
+
|
72
|
+
Segment symbol?(m) :=
|
73
|
+
@flags any? [f]:
|
74
|
+
f match:
|
75
|
+
Symbol -> f character == m
|
76
|
+
_ -> false
|
77
|
+
|
78
|
+
Segment precision := do:
|
79
|
+
@flags each [f]:
|
80
|
+
when(f is-a?(Precision)):
|
81
|
+
return(f value)
|
82
|
+
|
83
|
+
nil
|
84
|
+
|
85
|
+
Segment zero-pad? :=
|
86
|
+
@flags any? [f]:
|
87
|
+
f match:
|
88
|
+
ZeroPad -> true
|
89
|
+
_ -> false
|
@@ -0,0 +1,345 @@
|
|
1
|
+
namespace(atomy/format)
|
2
|
+
|
3
|
+
module(Atomy::Format):
|
4
|
+
class(Formatter):
|
5
|
+
attr-accessor(#position)
|
6
|
+
|
7
|
+
export:
|
8
|
+
initialize(@line, @segments) :=
|
9
|
+
reset!
|
10
|
+
|
11
|
+
bytecode(g) := do:
|
12
|
+
pos(g)
|
13
|
+
construct(g)
|
14
|
+
|
15
|
+
reset! := do:
|
16
|
+
@input = []
|
17
|
+
@output = ""
|
18
|
+
@position = 0
|
19
|
+
@stop? = false
|
20
|
+
@iterating = []
|
21
|
+
|
22
|
+
scan(*@input) := do:
|
23
|
+
@segments each [s]:
|
24
|
+
s match:
|
25
|
+
Break ? symbol?(".") -> do:
|
26
|
+
when(@iterating empty?):
|
27
|
+
@stop? = true
|
28
|
+
break
|
29
|
+
|
30
|
+
Break ->
|
31
|
+
when(next-inputs empty?):
|
32
|
+
break
|
33
|
+
|
34
|
+
_ -> process(s)
|
35
|
+
|
36
|
+
@output
|
37
|
+
|
38
|
+
export-to(atomy):
|
39
|
+
format(*inputs) := do:
|
40
|
+
res = scan(*inputs)
|
41
|
+
reset!
|
42
|
+
res
|
43
|
+
|
44
|
+
-- TODO: use alias-method
|
45
|
+
self [*inputs] := format(*inputs)
|
46
|
+
|
47
|
+
peek-input := @input [@position]
|
48
|
+
|
49
|
+
next-input := do:
|
50
|
+
v = peek-input
|
51
|
+
@position += 1
|
52
|
+
v
|
53
|
+
|
54
|
+
next-inputs := @input [@position .. -1] || []
|
55
|
+
|
56
|
+
process(c: Chunk) :=
|
57
|
+
@output << c text
|
58
|
+
|
59
|
+
process(s: String) :=
|
60
|
+
@output << justified(s, next-input to-s, true)
|
61
|
+
|
62
|
+
process(i: Decimal) :=
|
63
|
+
@output << integer(i, 10)
|
64
|
+
|
65
|
+
process(i: Hex) :=
|
66
|
+
@output << integer(i, 16)
|
67
|
+
|
68
|
+
process(i: Octal) :=
|
69
|
+
@output << integer(i, 8)
|
70
|
+
|
71
|
+
process(i: Binary) :=
|
72
|
+
@output << integer(i, 2)
|
73
|
+
|
74
|
+
process(i: Radix) :=
|
75
|
+
@output << integer(i, i precision)
|
76
|
+
|
77
|
+
process(f: Float) :=
|
78
|
+
@output << float(f, "f")
|
79
|
+
|
80
|
+
process(f: Exponent) :=
|
81
|
+
@output << float(f, "e")
|
82
|
+
|
83
|
+
process(f: General) :=
|
84
|
+
@output << float(f, "g")
|
85
|
+
|
86
|
+
process(c: Character) :=
|
87
|
+
@output << char(c, next-input)
|
88
|
+
|
89
|
+
process(a: Any) :=
|
90
|
+
@output << justified(a, next-input show, true)
|
91
|
+
|
92
|
+
process(p: Pluralize) := do:
|
93
|
+
word = sub-format(p singular)
|
94
|
+
num =
|
95
|
+
if(p symbol?(">"))
|
96
|
+
then: peek-input
|
97
|
+
else: next-input
|
98
|
+
|
99
|
+
condition:
|
100
|
+
num == 1 ->
|
101
|
+
@output << word
|
102
|
+
|
103
|
+
p plural ->
|
104
|
+
@output << sub-format(p plural)
|
105
|
+
|
106
|
+
otherwise ->
|
107
|
+
@output << pluralize(word)
|
108
|
+
|
109
|
+
process(l: Lowercase) :=
|
110
|
+
@output << sub-format(l content) downcase
|
111
|
+
|
112
|
+
process(c: Capitalize) := do:
|
113
|
+
words = sub-format(c content) split(" ")
|
114
|
+
number(c, words size) times [n]:
|
115
|
+
unless(n == 0):
|
116
|
+
@output << " "
|
117
|
+
|
118
|
+
@output << words shift capitalize
|
119
|
+
|
120
|
+
unless(words empty?):
|
121
|
+
@output << " " + words join(" ")
|
122
|
+
|
123
|
+
process(u: Uppercase) :=
|
124
|
+
@output << sub-format(u content) upcase
|
125
|
+
|
126
|
+
process(s: Skip) :=
|
127
|
+
if(s symbol?("<"))
|
128
|
+
then: @position -= number(s)
|
129
|
+
else: @position += number(s)
|
130
|
+
|
131
|
+
process(i: Indirection) :=
|
132
|
+
if(i symbol?("*"))
|
133
|
+
then:
|
134
|
+
@output << sub-format(next-input)
|
135
|
+
else:
|
136
|
+
@output << next-input format(*next-input)
|
137
|
+
|
138
|
+
process(i: Iterate) := do:
|
139
|
+
splat? = i symbol?("*")
|
140
|
+
sub? = i symbol?(".")
|
141
|
+
always-run? = i symbol?("+")
|
142
|
+
iterations = number(i, nil)
|
143
|
+
|
144
|
+
inputs =
|
145
|
+
if(splat?)
|
146
|
+
then: next-inputs
|
147
|
+
else: next-input
|
148
|
+
|
149
|
+
before = [@input, @position]
|
150
|
+
|
151
|
+
when(inputs empty? && always-run? &&
|
152
|
+
iterations != 0):
|
153
|
+
@output << sub-format(i content)
|
154
|
+
return(nil)
|
155
|
+
|
156
|
+
iterations match:
|
157
|
+
nil ->
|
158
|
+
if(sub?)
|
159
|
+
then:
|
160
|
+
@iterating = inputs
|
161
|
+
inputs each [is]:
|
162
|
+
@output << i content format(*is)
|
163
|
+
else:
|
164
|
+
@input = inputs
|
165
|
+
@position = 0
|
166
|
+
iterate(i content)
|
167
|
+
n -> do:
|
168
|
+
@input = inputs
|
169
|
+
@position = 0
|
170
|
+
iterate-max(n, i content)
|
171
|
+
|
172
|
+
if(splat?)
|
173
|
+
then: @position = @input size
|
174
|
+
else: [@input, @position] = before
|
175
|
+
|
176
|
+
process(c: Conditional) :=
|
177
|
+
[c symbol?("?"), c branches] match:
|
178
|
+
[true, t . (f . _)] ->
|
179
|
+
@output << sub-format(if(next-input) then: t; else: f)
|
180
|
+
|
181
|
+
[true, [t]] ->
|
182
|
+
when(next-input):
|
183
|
+
@output << sub-format(t)
|
184
|
+
|
185
|
+
_ -> do:
|
186
|
+
n = next-number(c)
|
187
|
+
if(n >= c branches size)
|
188
|
+
then:
|
189
|
+
when(c default):
|
190
|
+
@output << sub-format(c default)
|
191
|
+
else:
|
192
|
+
@output << sub-format(c branches [n])
|
193
|
+
|
194
|
+
process(j: Justify) :=
|
195
|
+
@output <<
|
196
|
+
justify(j, j segments collect [s]: sub-format(s))
|
197
|
+
|
198
|
+
process(x) := raise("todo formatting: " + x inspect)
|
199
|
+
|
200
|
+
|
201
|
+
pluralize(s) :=
|
202
|
+
condition:
|
203
|
+
s =~ r"o$"(i) ->
|
204
|
+
s + "es"
|
205
|
+
|
206
|
+
s =~ r"[aeiou]$"(i) ->
|
207
|
+
s + "s"
|
208
|
+
|
209
|
+
s =~ r"(?<root>.+[aeiou])y$"(i) ->
|
210
|
+
s + "s"
|
211
|
+
|
212
|
+
s =~ r"(lay-by|stand-by)$"(i) ->
|
213
|
+
s + "s"
|
214
|
+
|
215
|
+
s =~ r"(.+)y$"(i) ->
|
216
|
+
$1 + "es"
|
217
|
+
|
218
|
+
s =~ r"(.+)us$"(i) ->
|
219
|
+
$1 + ""
|
220
|
+
|
221
|
+
s =~ r"(.+)sis$"(i) ->
|
222
|
+
$1 + "es"
|
223
|
+
|
224
|
+
s =~ r"(.+)(ex|ix)$"(i) ->
|
225
|
+
$1 + "ces"
|
226
|
+
|
227
|
+
s =~ r"(.+)(ss|sh|ch|dge)$"(i) ->
|
228
|
+
s + "es"
|
229
|
+
|
230
|
+
otherwise ->
|
231
|
+
s + "s"
|
232
|
+
|
233
|
+
|
234
|
+
iterate(f) :=
|
235
|
+
until(next-inputs empty? || @stop?):
|
236
|
+
@output << sub-format(f)
|
237
|
+
|
238
|
+
iterate-max(max, f) :=
|
239
|
+
max times:
|
240
|
+
when(next-inputs empty? || @stop?):
|
241
|
+
break
|
242
|
+
|
243
|
+
@output << sub-format(f)
|
244
|
+
|
245
|
+
next-number(s) :=
|
246
|
+
number(s, nil) || next-input
|
247
|
+
|
248
|
+
char(c, n: Integer) := justified(c, n chr, true)
|
249
|
+
char(c, s) := justified(c, s to-s [0, 1], true)
|
250
|
+
|
251
|
+
integer(i, base) := justified(i, Integer(next-input) to-s(base))
|
252
|
+
|
253
|
+
float(f, x) := do:
|
254
|
+
format =
|
255
|
+
if(f precision)
|
256
|
+
then: "%." + f precision to-s + x
|
257
|
+
else: "%" + x
|
258
|
+
|
259
|
+
justified(f, sprintf(format, Float(next-input)))
|
260
|
+
|
261
|
+
sub-format(sub) := do:
|
262
|
+
sub position = @position
|
263
|
+
out = sub scan(*@input)
|
264
|
+
@position = sub position
|
265
|
+
sub reset!
|
266
|
+
out
|
267
|
+
|
268
|
+
number(s, default = 1) := do:
|
269
|
+
s flags each [f]:
|
270
|
+
when(f is-a?(Number)):
|
271
|
+
return(f value || next-inputs size)
|
272
|
+
|
273
|
+
default
|
274
|
+
|
275
|
+
justified(j, s, left? = false) :=
|
276
|
+
number(j, nil) match:
|
277
|
+
nil -> s
|
278
|
+
w -> do:
|
279
|
+
padding = if(j zero-pad?) then: "0"; else: " "
|
280
|
+
|
281
|
+
condition:
|
282
|
+
j symbol?("=") || j symbol?("<") && j symbol?(">") ->
|
283
|
+
s center(w, padding)
|
284
|
+
|
285
|
+
left? && !(j symbol?(">")) || j symbol?("<") ->
|
286
|
+
s ljust(w, padding)
|
287
|
+
|
288
|
+
otherwise ->
|
289
|
+
s rjust(w, padding)
|
290
|
+
|
291
|
+
justify(j, [s]) := justified(j, s)
|
292
|
+
justify(j, ss) :=
|
293
|
+
number(j, nil) match:
|
294
|
+
nil -> ss join
|
295
|
+
to -> justify-to(j, to, ss)
|
296
|
+
|
297
|
+
justify-to(_, _, []) := ""
|
298
|
+
justify-to(j, to, all: s . ss) := do:
|
299
|
+
needed = to - all collect(&#size) inject(*#"+")
|
300
|
+
|
301
|
+
spacings =
|
302
|
+
condition:
|
303
|
+
j symbol?("<") && j symbol?(">") || j symbol?("=") ->
|
304
|
+
all size + 1
|
305
|
+
|
306
|
+
j symbol?("<") ->
|
307
|
+
all size
|
308
|
+
|
309
|
+
j symbol?(">") ->
|
310
|
+
all size
|
311
|
+
|
312
|
+
otherwise ->
|
313
|
+
all size - 1
|
314
|
+
|
315
|
+
naive-average = needed / spacings
|
316
|
+
|
317
|
+
average =
|
318
|
+
if(needed - naive-average * spacings >= spacings - 1)
|
319
|
+
then: naive-average + 1
|
320
|
+
else: naive-average
|
321
|
+
|
322
|
+
condition:
|
323
|
+
j symbol?("<") || j symbol?("=") ->
|
324
|
+
[ " " * naive-average
|
325
|
+
s
|
326
|
+
" " * average
|
327
|
+
spaced(j, average, needed - naive-average - average, ss)
|
328
|
+
] join
|
329
|
+
|
330
|
+
otherwise ->
|
331
|
+
[ s
|
332
|
+
" " * naive-average
|
333
|
+
spaced(j, average, needed - naive-average, ss)
|
334
|
+
] join
|
335
|
+
|
336
|
+
spaced(_, _, _, []) := ""
|
337
|
+
spaced(j, _, left, [s]) :=
|
338
|
+
if(j symbol?(">") || j symbol?("="))
|
339
|
+
then: s + " " * left
|
340
|
+
else: " " * left + s
|
341
|
+
spaced(j, average, left, s . ss) :=
|
342
|
+
[ s
|
343
|
+
" " * average
|
344
|
+
spaced(j, average, left - average, ss)
|
345
|
+
] join
|