moron_text 1.0.0
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Future-1.md +64 -0
- data/Future-2.md +56 -0
- data/Gemfile +3 -0
- data/LICENSE +23 -0
- data/README.md +38 -0
- data/VERSION +1 -0
- data/lib/moron_text.rb +382 -0
- data/moron_text.gemspec +31 -0
- data/specs/0000-parse.rb +50 -0
- data/specs/0010-run.rb +24 -0
- data/specs/0011-run_Class.rb +52 -0
- data/specs/0012-typo.rb +102 -0
- data/specs/0020-grab_text.rb +51 -0
- data/specs/0021-grab_prev_text.rb +46 -0
- data/specs/0030-return.rb +42 -0
- data/specs/0031-next.rb +57 -0
- data/specs/lib/helpers.rb +8 -0
- metadata +133 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 144d379141e2a6c2b6f27a3225fcd1a62f7df66f
|
4
|
+
data.tar.gz: df899b273eceefcab7b505dcbb0829055f2d015a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 274084399d161b9ffb57091f3bbae6c49a0c6f1fad3b2202048736ee92b47c8706723923f508be7fbd7e45c702535742c07b8eb0084be043e5067b52e85d2842
|
7
|
+
data.tar.gz: 2ebae11133e9839546d02de3f6ba76f5e26fe871c2fc739c50605e47c408e3d5118baa404824bfc9d1a4020921d82162584f6a82787dab5b61289ce297986b22
|
data/.gitignore
ADDED
data/Future-1.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
|
2
|
+
# Syntax:
|
3
|
+
|
4
|
+
TEXT : InTRO
|
5
|
+
|
6
|
+
In the far east corner of the Galos Building...
|
7
|
+
|
8
|
+
TEXT : OuTRO ----------------
|
9
|
+
We end with a swipe towards the past...
|
10
|
+
!InTRO
|
11
|
+
!POS
|
12
|
+
--------------------------
|
13
|
+
|
14
|
+
ABOUT : first-guess
|
15
|
+
ON : apple
|
16
|
+
|
|
17
|
+
| PUT "100,100" into POS
|
18
|
+
|
|
19
|
+
| BLOCK : MOVE_CARD
|
20
|
+
| | SET the location of card button x to POS
|
21
|
+
| | ADD 15 to item 1 of POS
|
22
|
+
| |---
|
23
|
+
|
|
24
|
+
| REPEAT with x = 1 to the number of card buttons
|
25
|
+
| | MOVE_CARD .
|
26
|
+
| | PLAY_HARD .
|
27
|
+
| | DIE_HARD .
|
28
|
+
| | LIVE_HARD .
|
29
|
+
| |---
|
30
|
+
|
|
31
|
+
| REPEAT with x = 1 to the number of card buttons
|
32
|
+
| | SET the location of card button x to POS
|
33
|
+
| | ADD 15 to item 1 of POS
|
34
|
+
| |---
|
35
|
+
|
|
36
|
+
| ADD 15 to item 1 of POS
|
37
|
+
| ALERT : InTRO
|
38
|
+
| OuTRO .
|
39
|
+
| REPLACE : !POS POS
|
40
|
+
| REPLACE : !InTRO InTRO
|
41
|
+
| ALERT .
|
42
|
+
|---
|
43
|
+
|
44
|
+
ON FIRST TRUE :
|
45
|
+
|
|
46
|
+
| |---
|
47
|
+
| | ARR size > 2
|
48
|
+
| |---
|
49
|
+
| | DO SOMETHING .
|
50
|
+
| |---
|
51
|
+
|
|
52
|
+
| |---
|
53
|
+
| | ARR size < 5
|
54
|
+
| |---
|
55
|
+
| | DO SOMETHING ELSE.
|
56
|
+
| |---
|
57
|
+
|
|
58
|
+
| |---
|
59
|
+
| | DEFAULT .
|
60
|
+
| |---
|
61
|
+
|
|
62
|
+
|---
|
63
|
+
|
64
|
+
|
data/Future-2.md
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
## Example: Form that saves a record.
|
3
|
+
|
4
|
+
Which is your favorite fruit?
|
5
|
+
( ) apple
|
6
|
+
| I like apples too!
|
7
|
+
( ) orange
|
8
|
+
| I hate oranges. Blah!
|
9
|
+
( ) round
|
10
|
+
| ( ) prune
|
11
|
+
| ( ) raisin
|
12
|
+
| ( ) grapes
|
13
|
+
MERGE : previous answers
|
14
|
+
( ) Other.
|
15
|
+
|* Other: [___]
|
16
|
+
|
17
|
+
{ Send }
|
18
|
+
|
19
|
+
WHEN : Other: [xxx]
|
20
|
+
Cool... I never heard of ^X.
|
21
|
+
|
22
|
+
## Example: Arithmetic Calculator
|
23
|
+
|
24
|
+
[| STACK |]
|
25
|
+
{ 0 ... 9 . } || { + }
|
26
|
+
|| { - }
|
27
|
+
|| { * }
|
28
|
+
|| { / }
|
29
|
+
=======================
|
30
|
+
{ = }
|
31
|
+
|
32
|
+
ON : CLICK NUMBER
|
33
|
+
VALUE -> [| STACK |] .
|
34
|
+
" " -> [| STACK |] .
|
35
|
+
|
36
|
+
ON : CLICK NON-ALPHA-NUM AND NOT =
|
37
|
+
VALUE -> [| STACK |] .
|
38
|
+
" " -> [| STACK |] .
|
39
|
+
|
40
|
+
ON : CLICK =
|
41
|
+
MATH-EVAL : LAST 3 .
|
42
|
+
|
43
|
+
## Example:
|
44
|
+
|
45
|
+
ON : any wrong answer
|
46
|
+
SHOW PICTURE : http://www..../failure.jpg
|
47
|
+
|
48
|
+
ON : final right answer
|
49
|
+
|
50
|
+
You got it! Wait while I show you your secret
|
51
|
+
admirer.
|
52
|
+
|
53
|
+
Please wait a few seconds:
|
54
|
+
|
55
|
+
WAIT AND SHOW CLOCK : 5 secs
|
56
|
+
GO TO : http://www../admirer
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
|
2
|
+
Copyright (c) 2014 da99
|
3
|
+
|
4
|
+
MIT License
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
a copy of this software and associated documentation files (the
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
|
2
|
+
# Note:
|
3
|
+
|
4
|
+
This is not ready yet.
|
5
|
+
|
6
|
+
# Installation
|
7
|
+
|
8
|
+
gem 'moron_text'
|
9
|
+
|
10
|
+
# Moron\_Text
|
11
|
+
|
12
|
+
## Example: Menu
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
moron = Moron_Text.new(<<-EOF)
|
16
|
+
CORRECT /*
|
17
|
+
This is the right answer.
|
18
|
+
WRONG /*
|
19
|
+
This is the 1st wrong answer.
|
20
|
+
WRONG /*
|
21
|
+
This is the 2nd wrong answer.
|
22
|
+
WRONG /*
|
23
|
+
This is the 3rd wrong answer.
|
24
|
+
ON /* CORRECT
|
25
|
+
You chose wisely.
|
26
|
+
EOF
|
27
|
+
|
28
|
+
moron.run do | name, line, text |
|
29
|
+
case name
|
30
|
+
when 'CORRECT'
|
31
|
+
when 'WRONG'
|
32
|
+
when 'ON'
|
33
|
+
else
|
34
|
+
text.next
|
35
|
+
end
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
data/lib/moron_text.rb
ADDED
@@ -0,0 +1,382 @@
|
|
1
|
+
|
2
|
+
require "about_pos"
|
3
|
+
|
4
|
+
class Moron_Text
|
5
|
+
|
6
|
+
SPACE = ' '.freeze
|
7
|
+
DELIM = "\\s+" + Regexp.escape("/*")
|
8
|
+
|
9
|
+
PATTERNS = {
|
10
|
+
:command__arg => [
|
11
|
+
%r!\A\s*(.+)#{DELIM}\s+(.+)\Z!,
|
12
|
+
:value, :arg
|
13
|
+
],
|
14
|
+
|
15
|
+
:command => [
|
16
|
+
/\A\s*(.+)#{DELIM}\s*\Z/,
|
17
|
+
:value
|
18
|
+
]
|
19
|
+
}
|
20
|
+
|
21
|
+
NEW_LINE_REG_EXP = /\r?\n/
|
22
|
+
NL = "\n".freeze
|
23
|
+
TYPO = Class.new(RuntimeError) do
|
24
|
+
|
25
|
+
attr_reader :moron, :line_number
|
26
|
+
|
27
|
+
def initialize moron, text = "Typo"
|
28
|
+
super text
|
29
|
+
@moron = moron
|
30
|
+
@line_number = @moron.line_number
|
31
|
+
end
|
32
|
+
|
33
|
+
def line
|
34
|
+
moron.lines[line_number-1]
|
35
|
+
end
|
36
|
+
|
37
|
+
def line_context
|
38
|
+
start = line_number - 3 - 1
|
39
|
+
stop = line_number + 3 - 1
|
40
|
+
start = 0 if start < 0
|
41
|
+
i = start
|
42
|
+
moron.lines.slice(start, stop - start).map { |o|
|
43
|
+
i += 1
|
44
|
+
[i, o]
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end # === Class TYPO
|
48
|
+
|
49
|
+
MISSING_KEY = lambda { |hash, key|
|
50
|
+
fail RuntimeError, "Missing key: #{key.inspect}"
|
51
|
+
}
|
52
|
+
|
53
|
+
class << self
|
54
|
+
|
55
|
+
def standard_name n
|
56
|
+
n.split.map(&:upcase).join(SPACE)
|
57
|
+
end
|
58
|
+
|
59
|
+
def run *args
|
60
|
+
case
|
61
|
+
|
62
|
+
when block_given?
|
63
|
+
@run_lambda = lambda { |*args| yield(*args) }
|
64
|
+
|
65
|
+
when args.size == 3
|
66
|
+
args.last.next unless @run_lambda
|
67
|
+
@run_lambda.call(*args)
|
68
|
+
|
69
|
+
when args.size == 1 && args.first.is_a?(Proc)
|
70
|
+
@run_lambda = args.first
|
71
|
+
|
72
|
+
else
|
73
|
+
fail "Unknown args: #{args.inspect}"
|
74
|
+
|
75
|
+
end # === case
|
76
|
+
end
|
77
|
+
|
78
|
+
end # === class self ===
|
79
|
+
|
80
|
+
attr_reader :lines, :parsed_lines, :stack, :defs
|
81
|
+
|
82
|
+
def initialize str
|
83
|
+
@str = str
|
84
|
+
@lines = nil
|
85
|
+
@parsed_lines = nil
|
86
|
+
@stack = nil
|
87
|
+
@has_run = false
|
88
|
+
@defs = {}
|
89
|
+
@line_number = nil
|
90
|
+
@parsed_line_number = nil
|
91
|
+
@next_parse_line = nil
|
92
|
+
@settings = {}
|
93
|
+
@settings.default_proc = MISSING_KEY
|
94
|
+
end # === def initialize
|
95
|
+
|
96
|
+
def turn_on sym
|
97
|
+
@settings[sym] = true
|
98
|
+
end
|
99
|
+
|
100
|
+
def turn_off sym
|
101
|
+
@settings[sym] = false
|
102
|
+
end
|
103
|
+
|
104
|
+
def on? sym
|
105
|
+
if !@settings.has_key?(sym)
|
106
|
+
@settings[sym] = false
|
107
|
+
end
|
108
|
+
|
109
|
+
if @settings[sym] != true && @settings[sym] != false
|
110
|
+
fail "Invalid type for: #{sym.inspect}: #{settings[sym].inspect}"
|
111
|
+
end
|
112
|
+
|
113
|
+
@settings[sym]
|
114
|
+
end
|
115
|
+
|
116
|
+
def off? sym
|
117
|
+
if !@settings.has_key?(sym)
|
118
|
+
@settings[sym] = false
|
119
|
+
end
|
120
|
+
|
121
|
+
!on?[sym]
|
122
|
+
end
|
123
|
+
|
124
|
+
def []= k, v
|
125
|
+
@settings[k] = v
|
126
|
+
end
|
127
|
+
|
128
|
+
def [] k
|
129
|
+
@settings[k]
|
130
|
+
end
|
131
|
+
|
132
|
+
def typo!
|
133
|
+
throw :moron_flow, :typo
|
134
|
+
end
|
135
|
+
|
136
|
+
def typo msg
|
137
|
+
TYPO.new(self, msg)
|
138
|
+
end
|
139
|
+
|
140
|
+
def line_number
|
141
|
+
current[:line_number]
|
142
|
+
end
|
143
|
+
|
144
|
+
def current
|
145
|
+
@parsed_lines[@parsed_line_number]
|
146
|
+
end
|
147
|
+
|
148
|
+
def grab_prev_text
|
149
|
+
prev_text
|
150
|
+
end
|
151
|
+
|
152
|
+
def prev_text
|
153
|
+
fail typo("Missing previous text for line.") unless @seq.prev?
|
154
|
+
prev = @seq.prev.value
|
155
|
+
fail typo("Missing previous text for line.") unless prev[:type] == :text
|
156
|
+
prev[:value]
|
157
|
+
end
|
158
|
+
|
159
|
+
def grab_text
|
160
|
+
val = text
|
161
|
+
@seq.grab
|
162
|
+
val
|
163
|
+
end
|
164
|
+
|
165
|
+
def text
|
166
|
+
fail typo("Missing text for line.") unless @seq.next?
|
167
|
+
|
168
|
+
next_ = @seq.next.value
|
169
|
+
fail typo("Missing text for line.") unless next_[:type] == :text
|
170
|
+
|
171
|
+
next_[:value]
|
172
|
+
end
|
173
|
+
|
174
|
+
def split
|
175
|
+
current[:arg].split
|
176
|
+
end
|
177
|
+
|
178
|
+
def numbers
|
179
|
+
split.map { |u|
|
180
|
+
begin
|
181
|
+
Float(u)
|
182
|
+
rescue ArguementError
|
183
|
+
fail typo("Numerical typo.")
|
184
|
+
end
|
185
|
+
}
|
186
|
+
end
|
187
|
+
|
188
|
+
def fulfills? cond
|
189
|
+
parsed = current
|
190
|
+
About_Pos.Forward(cond).all? { |v,i,m|
|
191
|
+
args = m.grab
|
192
|
+
case v
|
193
|
+
when :on
|
194
|
+
args.any? { |on_name| on?(on_name) }
|
195
|
+
when :value
|
196
|
+
args.any? { |v| parsed[:value] == v }
|
197
|
+
else
|
198
|
+
fail "Typo: #{v.inspect}"
|
199
|
+
end
|
200
|
+
}
|
201
|
+
end
|
202
|
+
|
203
|
+
def next
|
204
|
+
throw :moron_flow, :next
|
205
|
+
end
|
206
|
+
|
207
|
+
def return *args
|
208
|
+
@stack.concat args
|
209
|
+
throw :moron_flow, :ignore
|
210
|
+
end
|
211
|
+
|
212
|
+
def run l = nil
|
213
|
+
return @stack if @has_run
|
214
|
+
|
215
|
+
parse
|
216
|
+
@stack = []
|
217
|
+
@line_number = 0
|
218
|
+
|
219
|
+
About_Pos.Forward(@parsed_lines) { |line, i, m|
|
220
|
+
@parsed_line_number = i
|
221
|
+
@seq = m
|
222
|
+
|
223
|
+
case line[:type]
|
224
|
+
|
225
|
+
when :text
|
226
|
+
line
|
227
|
+
|
228
|
+
when :command
|
229
|
+
if line.has_key?(:grab_all_text)
|
230
|
+
line[:text] = [
|
231
|
+
(line.has_key?(:text) ? line[:text] : nil),
|
232
|
+
(m.next? && m.next.value[:type] == :text ? m.grab[:value] : nil)
|
233
|
+
].
|
234
|
+
compact.
|
235
|
+
join(NL)
|
236
|
+
end
|
237
|
+
|
238
|
+
if line.has_key?(:allow)
|
239
|
+
case
|
240
|
+
when line[:allow].all? { |c| c.is_a?(Array) }
|
241
|
+
line[:allow].detect { |cond,i,m| fulfills? cond }
|
242
|
+
else
|
243
|
+
fulfills? line[:allow]
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
args = [line[:value], line, self]
|
248
|
+
val = nil
|
249
|
+
do_next = :next
|
250
|
+
|
251
|
+
if block_given?
|
252
|
+
do_next = catch(:moron_flow) { val = yield(*args) }
|
253
|
+
end
|
254
|
+
|
255
|
+
if do_next == :next && l
|
256
|
+
do_next = catch(:moron_flow) { val = l.call(*args) }
|
257
|
+
end
|
258
|
+
|
259
|
+
if do_next == :next
|
260
|
+
do_next = catch(:moron_flow) { val = self.class.run(*args) }
|
261
|
+
end
|
262
|
+
|
263
|
+
fail(typo "Typo: #{line[:value]}") if do_next == :typo || do_next == :next
|
264
|
+
(@stack << val) unless do_next == :ignore
|
265
|
+
|
266
|
+
else
|
267
|
+
fail "Programmer error: #{line[:type].inspect}"
|
268
|
+
|
269
|
+
end # case line[:type]
|
270
|
+
|
271
|
+
} # === About_Pos
|
272
|
+
|
273
|
+
@has_run = true
|
274
|
+
@stack
|
275
|
+
end
|
276
|
+
|
277
|
+
def meta_line type, val
|
278
|
+
{
|
279
|
+
:type =>type,
|
280
|
+
:value =>val,
|
281
|
+
:original =>@lines[@parse_index-1],
|
282
|
+
:line_number =>@parse_index,
|
283
|
+
:is_closed =>false,
|
284
|
+
:arg =>nil
|
285
|
+
}
|
286
|
+
end
|
287
|
+
|
288
|
+
def parse
|
289
|
+
return @parsed_lines if @parsed_lines
|
290
|
+
|
291
|
+
@lines = @str.split(NEW_LINE_REG_EXP)
|
292
|
+
@parse_index = 0
|
293
|
+
|
294
|
+
@parsed_lines = []
|
295
|
+
|
296
|
+
# === Pass 1: Create an array of commands and text.
|
297
|
+
@lines.each { |line|
|
298
|
+
|
299
|
+
@parse_index += 1
|
300
|
+
parsed = nil
|
301
|
+
|
302
|
+
is_command = PATTERNS.detect { |name, pattern|
|
303
|
+
match = line.match pattern.first
|
304
|
+
next unless match
|
305
|
+
captures = match.captures
|
306
|
+
shift_capture = lambda {
|
307
|
+
fail "Captures already empty: #{name.inspect}" if captures.empty?
|
308
|
+
captures.shift
|
309
|
+
}
|
310
|
+
|
311
|
+
parsed = meta_line(:command, nil)
|
312
|
+
start = 0
|
313
|
+
step = start
|
314
|
+
stop = pattern.size
|
315
|
+
capture_i = 0
|
316
|
+
while step < (stop-1)
|
317
|
+
grab_next = lambda {
|
318
|
+
step += 1
|
319
|
+
fail("No more items.") if step >= stop
|
320
|
+
pattern[step]
|
321
|
+
}
|
322
|
+
|
323
|
+
val = grab_next.call
|
324
|
+
next if step == start
|
325
|
+
|
326
|
+
case val
|
327
|
+
|
328
|
+
when :value
|
329
|
+
if parsed[:type] == :command
|
330
|
+
parsed[:value] = Moron_Text.standard_name shift_capture.call
|
331
|
+
else
|
332
|
+
parsed[:value] = shift_capture.call
|
333
|
+
end
|
334
|
+
|
335
|
+
when :arg
|
336
|
+
parsed[:arg] = shift_capture.call
|
337
|
+
|
338
|
+
when :is_closed
|
339
|
+
parsed[:is_closed] = true
|
340
|
+
|
341
|
+
when :allow
|
342
|
+
parsed[:allow] = grab_next.call
|
343
|
+
|
344
|
+
when :grab_all_text
|
345
|
+
parsed[:grab_all_text] = true
|
346
|
+
parsed[:text] = captures.compact.join ' '.freeze
|
347
|
+
|
348
|
+
else
|
349
|
+
fail "Typo: unknown pattern command: #{val.inspect}"
|
350
|
+
|
351
|
+
end # case val
|
352
|
+
end # while step < stop
|
353
|
+
|
354
|
+
match
|
355
|
+
} # detect if is command?
|
356
|
+
|
357
|
+
if !is_command
|
358
|
+
parsed = meta_line(:text, line.dup)
|
359
|
+
end
|
360
|
+
|
361
|
+
parsed.default_proc = MISSING_KEY
|
362
|
+
@parsed_lines << parsed
|
363
|
+
|
364
|
+
}
|
365
|
+
|
366
|
+
# === PASS 2: combine text, strip it
|
367
|
+
lines = []
|
368
|
+
About_Pos.Forward(@parsed_lines) { |o, i, m|
|
369
|
+
if o[:type] == :text
|
370
|
+
while m.next? && m.next.value[:type] == :text
|
371
|
+
o[:value] << NL
|
372
|
+
o[:value] << m.grab[:value]
|
373
|
+
end
|
374
|
+
o[:value].strip!
|
375
|
+
end
|
376
|
+
lines << o
|
377
|
+
}
|
378
|
+
|
379
|
+
@parsed_lines = lines
|
380
|
+
end # === def parse
|
381
|
+
|
382
|
+
end # === class Moron_Text ===
|
data/moron_text.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "moron_text"
|
7
|
+
spec.version = `cat VERSION`
|
8
|
+
spec.authors = ["da99"]
|
9
|
+
spec.email = ["i-hate-spam-1234567@mailinator.com"]
|
10
|
+
spec.summary = %q{A DSL for WWW mini-apps.}
|
11
|
+
spec.description = %q{
|
12
|
+
It won't be useful for you. It is meant to be
|
13
|
+
used with the WWW_App gem.
|
14
|
+
}
|
15
|
+
spec.homepage = "https://github.com/da99/moron_text"
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |file|
|
19
|
+
file.index('bin/') == 0 && file != "bin/#{File.basename Dir.pwd}"
|
20
|
+
}
|
21
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
25
|
+
spec.add_dependency "about_pos" , ">= 2.0.0"
|
26
|
+
|
27
|
+
spec.add_development_dependency "pry" , "~> 0.9"
|
28
|
+
spec.add_development_dependency "bundler" , "~> 1.5"
|
29
|
+
spec.add_development_dependency "bacon" , "~> 1.0"
|
30
|
+
spec.add_development_dependency "Bacon_Colored" , "~> 0.1"
|
31
|
+
end
|
data/specs/0000-parse.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
|
2
|
+
describe ":parse" do
|
3
|
+
|
4
|
+
it "returns an array describing the text" do
|
5
|
+
o = Moron_Text.new(<<-EOF)
|
6
|
+
MENU /* My Title
|
7
|
+
This is some text.
|
8
|
+
MENU2 /* arg arg
|
9
|
+
MENU3 /*
|
10
|
+
More text
|
11
|
+
content.
|
12
|
+
EOF
|
13
|
+
|
14
|
+
o.parse.map { |line|
|
15
|
+
line.values_at(:type, :value, :arg)
|
16
|
+
}.should == [
|
17
|
+
[:command, 'MENU', 'My Title'],
|
18
|
+
[:text, "This is some text.", nil],
|
19
|
+
[:command, 'MENU2', 'arg arg'],
|
20
|
+
[:command, 'MENU3', nil],
|
21
|
+
[:text, "More text\n content.", nil]
|
22
|
+
]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "parses stripped text" do
|
26
|
+
o = Moron_Text.new(trim <<-EOF)
|
27
|
+
CMD /*
|
28
|
+
some text
|
29
|
+
EOF
|
30
|
+
|
31
|
+
o.parse.map { |line|
|
32
|
+
line.values_at(:type, :value)
|
33
|
+
}.should == [
|
34
|
+
[:command, 'CMD'],
|
35
|
+
[:text, 'some text']
|
36
|
+
]
|
37
|
+
end
|
38
|
+
|
39
|
+
it "parses the following as a command: CMD :" do
|
40
|
+
o = Moron_Text.new(<<-EOF)
|
41
|
+
CMD /*
|
42
|
+
some text
|
43
|
+
EOF
|
44
|
+
|
45
|
+
o.parse.map { |line|
|
46
|
+
line[:type]
|
47
|
+
}.should == [:command, :text]
|
48
|
+
end
|
49
|
+
|
50
|
+
end # === describe ":parse"
|
data/specs/0010-run.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
describe :run do
|
3
|
+
|
4
|
+
it "runs the text as code" do
|
5
|
+
o = Moron_Text.new(<<-EOF)
|
6
|
+
ADD /* 1 2 3
|
7
|
+
SUBTRACT /* 100 5 5
|
8
|
+
EOF
|
9
|
+
|
10
|
+
o.run do |name, line, moron|
|
11
|
+
case name
|
12
|
+
when 'ADD'
|
13
|
+
moron.numbers.reduce(:+)
|
14
|
+
when 'SUBTRACT'
|
15
|
+
moron.numbers.reduce(:-)
|
16
|
+
else
|
17
|
+
moron.next
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
o.stack.should == [6, 90]
|
22
|
+
end
|
23
|
+
|
24
|
+
end # === describe ":run"
|
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
describe ":run Class" do
|
3
|
+
|
4
|
+
it "runs given block on commands" do
|
5
|
+
stack = []
|
6
|
+
|
7
|
+
c = Class.new(Moron_Text)
|
8
|
+
c.run { |name, line, moron|
|
9
|
+
stack << [name, line[:arg]]
|
10
|
+
}
|
11
|
+
|
12
|
+
o = c.new(<<-EOF)
|
13
|
+
BLADE_1 /* 1 2 3
|
14
|
+
BLADE_2 /* 100 5 5
|
15
|
+
EOF
|
16
|
+
|
17
|
+
o.run
|
18
|
+
|
19
|
+
stack.should == [
|
20
|
+
['BLADE_1', '1 2 3'],
|
21
|
+
['BLADE_2', '100 5 5'],
|
22
|
+
]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "runs given block if instance .run block uses .next" do
|
26
|
+
stack = []
|
27
|
+
|
28
|
+
c = Class.new(Moron_Text)
|
29
|
+
c.run { |name, line, moron|
|
30
|
+
stack << [name, line[:arg]]
|
31
|
+
}
|
32
|
+
|
33
|
+
o = c.new(<<-EOF)
|
34
|
+
BLADE_1 /* 1
|
35
|
+
BLADE_2 /* c
|
36
|
+
EOF
|
37
|
+
|
38
|
+
o.run { |name, line, moron|
|
39
|
+
stack << "instance"
|
40
|
+
moron.next
|
41
|
+
fail "Not reachable"
|
42
|
+
}
|
43
|
+
|
44
|
+
stack.should == [
|
45
|
+
"instance",
|
46
|
+
['BLADE_1', '1'],
|
47
|
+
"instance",
|
48
|
+
['BLADE_2', 'c'],
|
49
|
+
]
|
50
|
+
end
|
51
|
+
|
52
|
+
end # === describe ":run Class"
|
data/specs/0012-typo.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
|
2
|
+
describe :typo do
|
3
|
+
|
4
|
+
it "raises a TYPO" do
|
5
|
+
o = Moron_Text.new(<<-EOF)
|
6
|
+
DUCK /* a
|
7
|
+
Quack
|
8
|
+
EOF
|
9
|
+
|
10
|
+
lambda {
|
11
|
+
o.run do |name, line, moron|
|
12
|
+
fail moron.typo('blah') if name == 'DUCK'
|
13
|
+
end
|
14
|
+
}.should.raise(Moron_Text::TYPO).
|
15
|
+
message.should.match /blah/
|
16
|
+
end
|
17
|
+
|
18
|
+
it "returns a TYPO with :line" do
|
19
|
+
o = Moron_Text.new(trim <<-EOF)
|
20
|
+
GOOSE /*
|
21
|
+
Line 2
|
22
|
+
Line 3
|
23
|
+
DUCK /*
|
24
|
+
Line 5
|
25
|
+
EOF
|
26
|
+
|
27
|
+
lambda {
|
28
|
+
o.run do |name, line, moron|
|
29
|
+
case name
|
30
|
+
when 'GOOSE'
|
31
|
+
"done"
|
32
|
+
when 'DUCK'
|
33
|
+
fail moron.typo('blah 1')
|
34
|
+
else
|
35
|
+
moron.typo!
|
36
|
+
end
|
37
|
+
end
|
38
|
+
}.
|
39
|
+
should.raise(Moron_Text::TYPO).
|
40
|
+
line.should == 'DUCK /*'
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns a TYPO with :line_number" do
|
44
|
+
o = Moron_Text.new(<<-EOF)
|
45
|
+
GOOSE /*
|
46
|
+
Line 2
|
47
|
+
Line 3
|
48
|
+
DUCK /*
|
49
|
+
Line 5
|
50
|
+
EOF
|
51
|
+
|
52
|
+
lambda {
|
53
|
+
o.run do |name, line, moron|
|
54
|
+
case name
|
55
|
+
when 'GOOSE'
|
56
|
+
"done"
|
57
|
+
when 'DUCK'
|
58
|
+
fail moron.typo('blah 2')
|
59
|
+
else
|
60
|
+
moron.typo!
|
61
|
+
end
|
62
|
+
end
|
63
|
+
}.
|
64
|
+
should.raise(Moron_Text::TYPO).
|
65
|
+
line_number.should == 4
|
66
|
+
end # === it returns a TYPO with :line_number
|
67
|
+
|
68
|
+
it 'returns a TYPO with :line_context' do
|
69
|
+
o = Moron_Text.new(<<-EOF)
|
70
|
+
GOOSE /*
|
71
|
+
Line 2
|
72
|
+
Line 3
|
73
|
+
DUCK /*
|
74
|
+
Line 5
|
75
|
+
EOF
|
76
|
+
space = ' '
|
77
|
+
|
78
|
+
lambda {
|
79
|
+
o.run do |name, line, moron|
|
80
|
+
case name
|
81
|
+
when 'GOOSE'
|
82
|
+
"done"
|
83
|
+
when 'DUCK'
|
84
|
+
fail moron.typo('blah 3')
|
85
|
+
else
|
86
|
+
moron.typo!
|
87
|
+
end
|
88
|
+
end
|
89
|
+
}.
|
90
|
+
should.raise(Moron_Text::TYPO).
|
91
|
+
line_context.should == [
|
92
|
+
[1, "#{space}GOOSE /*"],
|
93
|
+
[2, "#{space} Line 2"],
|
94
|
+
[3, "#{space} Line 3"],
|
95
|
+
[4, "#{space}DUCK /*"],
|
96
|
+
[5, "#{space} Line 5"]
|
97
|
+
]
|
98
|
+
end
|
99
|
+
|
100
|
+
end # === describe :typo
|
101
|
+
|
102
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
|
2
|
+
describe :grab_text do
|
3
|
+
|
4
|
+
it "pops the following text" do
|
5
|
+
o = Moron_Text.new(<<-EOF)
|
6
|
+
DUCK /*
|
7
|
+
QUACK
|
8
|
+
BIRD /*
|
9
|
+
Whistle
|
10
|
+
EOF
|
11
|
+
|
12
|
+
sounds = []
|
13
|
+
o.run do |name, line, moron|
|
14
|
+
case name
|
15
|
+
when 'DUCK'
|
16
|
+
sounds << moron.grab_text
|
17
|
+
when 'BIRD'
|
18
|
+
sounds << moron.grab_text
|
19
|
+
else
|
20
|
+
moron.typo!
|
21
|
+
end
|
22
|
+
end
|
23
|
+
sounds.should == %w{ QUACK Whistle }
|
24
|
+
end
|
25
|
+
|
26
|
+
it "pops multiple lines of text" do
|
27
|
+
o = Moron_Text.new(<<-EOF)
|
28
|
+
DUCK /*
|
29
|
+
|
30
|
+
This is one line.
|
31
|
+
|
32
|
+
This is another line.
|
33
|
+
|
34
|
+
EOF
|
35
|
+
|
36
|
+
txt = nil
|
37
|
+
o.run { |name, line, moron|
|
38
|
+
if name == 'DUCK'
|
39
|
+
txt = moron.grab_text
|
40
|
+
else
|
41
|
+
moron.typo!
|
42
|
+
end
|
43
|
+
}
|
44
|
+
txt.should == <<-EOF.strip
|
45
|
+
This is one line.
|
46
|
+
|
47
|
+
This is another line.
|
48
|
+
EOF
|
49
|
+
end
|
50
|
+
|
51
|
+
end # === describe :text
|
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
describe :grab_prev_text do
|
3
|
+
|
4
|
+
it "pops the previous text" do
|
5
|
+
o = Moron_Text.new(<<-EOF)
|
6
|
+
This is text.
|
7
|
+
BIRD /*
|
8
|
+
Whistle
|
9
|
+
EOF
|
10
|
+
|
11
|
+
sounds = []
|
12
|
+
o.run { |name, line, moron|
|
13
|
+
if name == 'BIRD'
|
14
|
+
sounds << moron.grab_prev_text
|
15
|
+
else
|
16
|
+
moron.typo!
|
17
|
+
end
|
18
|
+
}
|
19
|
+
sounds.should == ['This is text.']
|
20
|
+
end
|
21
|
+
|
22
|
+
it "pops multiple lines of text" do
|
23
|
+
o = Moron_Text.new(<<-EOF)
|
24
|
+
This is one line.
|
25
|
+
|
26
|
+
This is another line.
|
27
|
+
DUCK /*
|
28
|
+
|
29
|
+
EOF
|
30
|
+
|
31
|
+
txt = nil
|
32
|
+
o.run { |name, line, moron|
|
33
|
+
if name == 'DUCK'
|
34
|
+
txt = moron.grab_prev_text
|
35
|
+
else
|
36
|
+
moron.typo!
|
37
|
+
end
|
38
|
+
}
|
39
|
+
trim(txt).should == trim(<<-EOF)
|
40
|
+
This is one line.
|
41
|
+
|
42
|
+
This is another line.
|
43
|
+
EOF
|
44
|
+
end
|
45
|
+
|
46
|
+
end # === describe :text
|
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
describe :return do
|
3
|
+
|
4
|
+
it "concats arguments to stack" do
|
5
|
+
m = Moron_Text.new(<<-EOF)
|
6
|
+
MENU /* 1
|
7
|
+
MENU /* 2
|
8
|
+
MENU /* 3
|
9
|
+
EOF
|
10
|
+
|
11
|
+
m.run { |name, line, moron| moron.return line[:arg] }
|
12
|
+
m.stack.should == '1 2 3'.split
|
13
|
+
end # === it adds value to stack
|
14
|
+
|
15
|
+
it "prevents the rest of the block or lambda from executing" do
|
16
|
+
m = Moron_Text.new(<<-EOF)
|
17
|
+
MENU /* 4
|
18
|
+
MENU /* 5
|
19
|
+
MENU /* 6
|
20
|
+
EOF
|
21
|
+
|
22
|
+
m.run { |name, line, moron|
|
23
|
+
moron.return line[:arg]
|
24
|
+
fail
|
25
|
+
}
|
26
|
+
|
27
|
+
m.stack.should == '4 5 6'.split
|
28
|
+
end # === it prevents the rest of the block or lambda from executing
|
29
|
+
|
30
|
+
it "does not alter the stack if no arguments are passed to it" do
|
31
|
+
m = Moron_Text.new(<<-EOF)
|
32
|
+
MENU /* 4
|
33
|
+
MENU /* 5
|
34
|
+
MENU /* 6
|
35
|
+
EOF
|
36
|
+
|
37
|
+
m.run { |name, line, moron| moron.return }
|
38
|
+
|
39
|
+
m.stack.should == []
|
40
|
+
end # === it does not alter the stack if no arguments are passed to it
|
41
|
+
|
42
|
+
end # === describe :return
|
data/specs/0031-next.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
|
2
|
+
describe :next do
|
3
|
+
|
4
|
+
it "runs the given lambda if called from the :run block" do
|
5
|
+
m = Moron_Text.new(<<-EOF)
|
6
|
+
COMM 1 /* 1
|
7
|
+
COMM 2 /* 1
|
8
|
+
EOF
|
9
|
+
|
10
|
+
stack = []
|
11
|
+
l = lambda { |name, line, moron| stack << :lambda }
|
12
|
+
m.run(l) { |name, line, moron| stack << :block; moron.next }
|
13
|
+
stack.should == [:block, :lambda, :block, :lambda]
|
14
|
+
end
|
15
|
+
|
16
|
+
it "runs the given Class :run block if called from the instance :run lambda" do
|
17
|
+
stack = []
|
18
|
+
c = Class.new(Moron_Text)
|
19
|
+
c.run { |name, line, moron| stack << :class }
|
20
|
+
o = c.new(<<-EOF)
|
21
|
+
COMM 3 /* 2
|
22
|
+
COMM 4 /* 2
|
23
|
+
EOF
|
24
|
+
l = lambda { |name, line, moron| stack << :lambda; moron.next }
|
25
|
+
o.run(l)
|
26
|
+
stack.should == [:lambda, :class, :lambda, :class]
|
27
|
+
end
|
28
|
+
|
29
|
+
it "runs the given Class :run block if called from: instance :run lambda and block" do
|
30
|
+
stack = []
|
31
|
+
c = Class.new(Moron_Text)
|
32
|
+
c.run { |name, line, moron| stack << :class }
|
33
|
+
o = c.new(<<-EOF)
|
34
|
+
COMM 5 /* 3
|
35
|
+
COMM 6 /* 3
|
36
|
+
EOF
|
37
|
+
l = lambda { |name, line, moron| stack << :lambda; moron.next }
|
38
|
+
o.run(l) { |name, line, moron| stack << :block; moron.next }
|
39
|
+
stack.should == [:block, :lambda, :class, :block, :lambda, :class]
|
40
|
+
end
|
41
|
+
|
42
|
+
it "fails w/ Typo if :next is called in Class :run" do
|
43
|
+
stack = []
|
44
|
+
c = Class.new(Moron_Text)
|
45
|
+
c.run { |name, line, moron| moron.next if name == 'COMM 8' }
|
46
|
+
o = c.new(<<-EOF)
|
47
|
+
COMM 7 /* 4
|
48
|
+
COMM 8 /* 4
|
49
|
+
COMM 9 /* 4
|
50
|
+
EOF
|
51
|
+
lambda {
|
52
|
+
o.run { |name, line, moron| stack << :block; moron.next }
|
53
|
+
}.should.raise(Moron_Text::TYPO).
|
54
|
+
message.should =~ /Typo. COMM 8/
|
55
|
+
end
|
56
|
+
|
57
|
+
end # === describe :next
|
metadata
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: moron_text
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- da99
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-01-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: about_pos
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.0.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pry
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.9'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.9'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.5'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bacon
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: Bacon_Colored
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.1'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.1'
|
83
|
+
description: "\n It won't be useful for you. It is meant to be\n used with the
|
84
|
+
WWW_App gem.\n "
|
85
|
+
email:
|
86
|
+
- i-hate-spam-1234567@mailinator.com
|
87
|
+
executables: []
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- ".gitignore"
|
92
|
+
- Future-1.md
|
93
|
+
- Future-2.md
|
94
|
+
- Gemfile
|
95
|
+
- LICENSE
|
96
|
+
- README.md
|
97
|
+
- VERSION
|
98
|
+
- lib/moron_text.rb
|
99
|
+
- moron_text.gemspec
|
100
|
+
- specs/0000-parse.rb
|
101
|
+
- specs/0010-run.rb
|
102
|
+
- specs/0011-run_Class.rb
|
103
|
+
- specs/0012-typo.rb
|
104
|
+
- specs/0020-grab_text.rb
|
105
|
+
- specs/0021-grab_prev_text.rb
|
106
|
+
- specs/0030-return.rb
|
107
|
+
- specs/0031-next.rb
|
108
|
+
- specs/lib/helpers.rb
|
109
|
+
homepage: https://github.com/da99/moron_text
|
110
|
+
licenses:
|
111
|
+
- MIT
|
112
|
+
metadata: {}
|
113
|
+
post_install_message:
|
114
|
+
rdoc_options: []
|
115
|
+
require_paths:
|
116
|
+
- lib
|
117
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
requirements: []
|
128
|
+
rubyforge_project:
|
129
|
+
rubygems_version: 2.4.5
|
130
|
+
signing_key:
|
131
|
+
specification_version: 4
|
132
|
+
summary: A DSL for WWW mini-apps.
|
133
|
+
test_files: []
|