livetext 0.9.13 → 0.9.19
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 +4 -4
- data/README.lt3 +2 -2
- data/imports/bookish.rb +288 -0
- data/imports/calibre.rb +28 -0
- data/imports/livemagick.rb +133 -0
- data/imports/markdown.rb +44 -0
- data/imports/markdown_importable.rb +46 -0
- data/imports/pyggish.rb +204 -0
- data/imports/tutorial.rb +95 -0
- data/lib/cmdargs.rb +93 -0
- data/lib/errors.rb +4 -2
- data/lib/formatline.rb +56 -83
- data/lib/functions.rb +1 -1
- data/lib/handler/icanhaz.rb +35 -0
- data/lib/handler.rb +1 -0
- data/lib/helpers.rb +177 -4
- data/lib/livetext.rb +20 -139
- data/lib/parser/file.rb +8 -0
- data/lib/parser/general.rb +1 -1
- data/lib/parser/import.rb +1 -3
- data/lib/parser/mixin.rb +22 -30
- data/lib/parser/set.rb +35 -26
- data/lib/parser/string.rb +19 -4
- data/lib/processor.rb +2 -12
- data/lib/standard.rb +73 -107
- data/lib/userapi.rb +1 -4
- data/livetext.gemspec +2 -1
- data/plugin/bookish.rb +26 -22
- data/plugin/calibre.rb +1 -1
- data/plugin/livemagick.rb +10 -10
- data/plugin/markdown.rb +13 -11
- data/plugin/pyggish.rb +94 -84
- data/plugin/tutorial.rb +10 -5
- data/test/snapshots/OMIT.txt +11 -0
- data/test/snapshots/clusion.txt +84 -0
- data/test/snapshots/error_inc_line_num/match-error.txt +1 -0
- data/test/snapshots/error_invalid_name/match-error.txt +1 -0
- data/test/snapshots/error_line_num/match-error.txt +1 -0
- data/test/snapshots/error_mismatched_end/expected-output.txt +0 -2
- data/test/snapshots/error_mismatched_end/match-error.txt +1 -0
- data/test/snapshots/error_missing_end/match-error.txt +1 -0
- data/test/snapshots/error_no_such_copy/match-error.txt +1 -0
- data/test/snapshots/error_no_such_copy/source.lt3 +0 -1
- data/test/snapshots/error_no_such_inc/match-error.txt +1 -0
- data/test/snapshots/error_no_such_mixin/match-error.txt +1 -0
- data/test/snapshots/icanhaz/expected-output.txt +5 -0
- data/test/snapshots/icanhaz/match-error.txt +1 -0
- data/test/snapshots/icanhaz/simple_import.rb +5 -0
- data/test/snapshots/{error_no_such_mixin/actual-output.txt → icanhaz/source.lt3} +3 -4
- data/test/snapshots/{error_invalid_name/actual-output.txt → icanhaz2/expected-error.txt} +0 -0
- data/test/snapshots/icanhaz2/expected-output.txt +6 -0
- data/test/snapshots/icanhaz2/simple_canhaz.rb +5 -0
- data/test/snapshots/icanhaz2/source.lt3 +6 -0
- data/test/snapshots/predef_vars/match-output.txt +6 -0
- data/test/snapshots/{error_no_such_copy/actual-output.txt → simple_import/expected-error.txt} +0 -0
- data/test/snapshots/simple_import/expected-output.txt +7 -0
- data/test/snapshots/simple_import/simple_import.rb +5 -0
- data/test/snapshots/simple_import/source.lt3 +7 -0
- data/test/snapshots/simple_include/source.lt3 +0 -1
- data/test/snapshots.rb +103 -107
- data/test/unit/all.rb +1 -0
- data/test/unit/formatline.rb +650 -0
- data/test/unit/parser/general.rb +21 -21
- data/test/unit/parser/importable.rb +1 -1
- data/test/unit/parser/mixin.rb +2 -2
- data/test/unit/parser/set.rb +19 -12
- data/test/unit/parser/string.rb +14 -14
- data/test/unit/parser.rb +2 -0
- metadata +37 -46
- data/test/formatting.rb +0 -103
- data/test/snapshots/error_inc_line_num/actual-error.txt +0 -1
- data/test/snapshots/error_inc_line_num/actual-output.txt +0 -13
- data/test/snapshots/error_inc_line_num/expected-err-line1match.txt +0 -1
- data/test/snapshots/error_inc_line_num/out-sdiff.txt +0 -14
- data/test/snapshots/error_invalid_name/actual-error.txt +0 -10
- data/test/snapshots/error_invalid_name/expected-err-line1match.txt +0 -1
- data/test/snapshots/error_invalid_name/out-sdiff.txt +0 -6
- data/test/snapshots/error_line_num/actual-error.txt +0 -1
- data/test/snapshots/error_line_num/actual-output.txt +0 -5
- data/test/snapshots/error_line_num/expected-err-line1match.txt +0 -1
- data/test/snapshots/error_line_num/out-sdiff.txt +0 -6
- data/test/snapshots/error_mismatched_end/actual-error.txt +0 -1
- data/test/snapshots/error_mismatched_end/actual-output.txt +0 -8
- data/test/snapshots/error_mismatched_end/expected-err-line1match.txt +0 -1
- data/test/snapshots/error_mismatched_end/out-sdiff.txt +0 -9
- data/test/snapshots/error_missing_end/actual-error.txt +0 -1
- data/test/snapshots/error_missing_end/actual-output.txt +0 -5
- data/test/snapshots/error_missing_end/expected-err-line1match.txt +0 -1
- data/test/snapshots/error_missing_end/out-sdiff.txt +0 -6
- data/test/snapshots/error_name_not_permitted/OLD-exp-out +0 -4
- data/test/snapshots/error_no_such_copy/actual-error.txt +0 -10
- data/test/snapshots/error_no_such_copy/expected-err-line1match.txt +0 -1
- data/test/snapshots/error_no_such_copy/out-sdiff.txt +0 -5
- data/test/snapshots/error_no_such_inc/actual-error.txt +0 -10
- data/test/snapshots/error_no_such_inc/actual-output.txt +0 -0
- data/test/snapshots/error_no_such_inc/expected-err-line1match.txt +0 -1
- data/test/snapshots/error_no_such_inc/out-sdiff.txt +0 -6
- data/test/snapshots/error_no_such_mixin/actual-error.txt +0 -1
- data/test/snapshots/error_no_such_mixin/expected-err-line1match.txt +0 -1
- data/test/snapshots/error_no_such_mixin/out-sdiff.txt +0 -12
- data/test/snapshots/lines.txt +0 -124
- data/test/snapshots/predef_vars/actual-error.txt +0 -0
- data/test/snapshots/predef_vars/actual-output.txt +0 -6
- data/test/snapshots/predef_vars/expected-output.txt +0 -6
- data/test/snapshots/predef_vars/out-sdiff.txt +0 -7
- data/test/snapshots/simple_mixin/actual-error.txt +0 -2
- data/test/snapshots/simple_mixin/actual-output.txt +0 -4
- data/test/snapshots/simple_mixin/out-sdiff.txt +0 -6
- data/test/unit/parse_misc.rb +0 -60
- data/test/unit/parse_set.rb +0 -157
data/imports/pyggish.rb
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
require 'rouge'
|
|
2
|
+
|
|
3
|
+
module Pyggish
|
|
4
|
+
def self.pyg_change(code, klass, style)
|
|
5
|
+
color = style[0..6]
|
|
6
|
+
modifier = style[8]
|
|
7
|
+
mod_open = modifier ? "<#{modifier}>" : ""
|
|
8
|
+
mod_close = modifier ? "</#{modifier}>" : ""
|
|
9
|
+
rx = /<span class="#{klass}">(?<cname>[^<]+?)<\/span>/
|
|
10
|
+
loop do
|
|
11
|
+
md = rx.match(code)
|
|
12
|
+
break if md.nil?
|
|
13
|
+
str = md[:cname]
|
|
14
|
+
result = code.sub!(rx, "<font color=#{color}>#{mod_open}#{str}#{mod_close}</font>")
|
|
15
|
+
break if result.nil?
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self._codebar_color(lexer)
|
|
20
|
+
color = case lexer
|
|
21
|
+
when :elixir
|
|
22
|
+
"#fc88fc"
|
|
23
|
+
when :ruby
|
|
24
|
+
"#fc8888"
|
|
25
|
+
else
|
|
26
|
+
raise "Unknown lexer"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.pyg_finalize(code, lexer=:elixir)
|
|
31
|
+
Styles.each_pair {|klass, style| pyg_change(code, klass, style) }
|
|
32
|
+
File.open("debug-pf1", "w") {|f| f.puts code }
|
|
33
|
+
code.sub!(/<pre>/, "<pre>\n")
|
|
34
|
+
code.gsub!(/<span class="[np]">/, "")
|
|
35
|
+
code.gsub!(/<\/span>/, "")
|
|
36
|
+
color = _codebar_color(lexer)
|
|
37
|
+
code.sub!(/<td class="linenos"/, "<td width=2%></td><td width=5% bgcolor=#{color}")
|
|
38
|
+
code.gsub!(/<td/, "<td valign=top ")
|
|
39
|
+
code.gsub!(/ class="[^"]*?"/, "") # Get rid of remaining Pygments CSS
|
|
40
|
+
File.open("debug-pf2", "w") {|f| f.puts code }
|
|
41
|
+
lines = code.split("\n")
|
|
42
|
+
# lines.each {|line| line << "\n" }
|
|
43
|
+
n1 = lines.index {|x| x =~ /<pre>/ }
|
|
44
|
+
n2 = lines.index {|x| x =~ /<\/pre>/ }
|
|
45
|
+
# FIXME ?
|
|
46
|
+
n1 ||= 0
|
|
47
|
+
n2 ||= -1
|
|
48
|
+
lines[n1].sub!(/ 1$/, " 1 ")
|
|
49
|
+
(n1+1).upto(n2) {|n| lines[n].replace(" " + lines[n] + " ") }
|
|
50
|
+
code = lines.join("\n")
|
|
51
|
+
File.open("debug-pf3", "w") {|f| f.puts code }
|
|
52
|
+
code
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def _process_code(text)
|
|
56
|
+
File.open("debug-pc1", "w") {|f| f.puts text }
|
|
57
|
+
lines = text.split("\n")
|
|
58
|
+
lines = lines.select {|x| x !~ /##~ omit/ }
|
|
59
|
+
@refs = {}
|
|
60
|
+
lines.each.with_index do |line, i|
|
|
61
|
+
if line =~ /##~ ref/
|
|
62
|
+
frag, name = line.split(/ *##~ ref/)
|
|
63
|
+
@refs[name.strip] = i
|
|
64
|
+
line.replace(frag)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
lines.map! {|line| " " + line }
|
|
68
|
+
text2 = lines.join("\n")
|
|
69
|
+
File.open("debug-pc2", "w") {|f| f.puts text2 }
|
|
70
|
+
text.replace(text2)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def _colorize(code, lexer=:elixir)
|
|
74
|
+
text = ::Pygments.highlight(code, lexer: lexer, options: {linenos: "table"})
|
|
75
|
+
_debug "--- in _colorize: text = #{text.inspect}"
|
|
76
|
+
text2 = PygmentFix.pyg_finalize(text, lexer)
|
|
77
|
+
result = "<!-- colorized code -->\n" + text2
|
|
78
|
+
result
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def _colorize!(code, lexer=:elixir)
|
|
82
|
+
text = ::Pygments.highlight(code, lexer: lexer, options: {})
|
|
83
|
+
_debug "--- in _colorize!: text = #{text.inspect}"
|
|
84
|
+
text2 = PygmentFix.pyg_finalize(text, lexer)
|
|
85
|
+
result = "<!-- colorized code -->\n" + text2
|
|
86
|
+
result
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def OLD_ruby
|
|
90
|
+
file = @_args.first
|
|
91
|
+
if file.nil?
|
|
92
|
+
code = "# Ruby code\n"
|
|
93
|
+
_body {|line| code << line + "\n" }
|
|
94
|
+
else
|
|
95
|
+
code = "# Ruby code\n\n" + ::File.read(file)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
_process_code(code)
|
|
99
|
+
html = _colorize(code, :ruby)
|
|
100
|
+
_out "\n#{html}\n "
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def OLD_elixir
|
|
104
|
+
file = @_args.first
|
|
105
|
+
if file.nil?
|
|
106
|
+
code = ""
|
|
107
|
+
_body {|line| code << line + "\n" }
|
|
108
|
+
else
|
|
109
|
+
code = ::File.read(file)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
_process_code(code)
|
|
113
|
+
html = _colorize(code, :elixir)
|
|
114
|
+
_out "\n#{html}\n "
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def fragment
|
|
118
|
+
lang = @_args.empty? ? :elixir : @_args.first.to_sym # ruby or elixir
|
|
119
|
+
@_args = []
|
|
120
|
+
send(lang)
|
|
121
|
+
_out "\n"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def code # FIXME ?
|
|
125
|
+
text = ""
|
|
126
|
+
_body {|line| _out " " + line }
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def mono
|
|
130
|
+
_out "<pre>"
|
|
131
|
+
_body {|line| _out " " + line }
|
|
132
|
+
_out "</pre>"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def create_code_styles
|
|
136
|
+
dir = @_outdir || "."
|
|
137
|
+
theme, back = "Github", "white"
|
|
138
|
+
css = Rouge::Themes.const_get(theme).render(scope: '.rb_highlight')
|
|
139
|
+
added = <<~CSS
|
|
140
|
+
.rb_highlight {
|
|
141
|
+
font-family: 'Monaco', 'Andale Mono', 'Lucida Grande', 'Courier', 'Lucida Console', 'Courier New', monospace;
|
|
142
|
+
white-space: pre;
|
|
143
|
+
background-color: #{back}
|
|
144
|
+
}
|
|
145
|
+
CSS
|
|
146
|
+
|
|
147
|
+
css.gsub!(/{\n/, "{\n font-family: courier;")
|
|
148
|
+
css = added + "\n" + css
|
|
149
|
+
# STDERR.puts "Writing #{theme} theme to ruby.css"
|
|
150
|
+
File.write("#{dir}/ruby.css", css)
|
|
151
|
+
|
|
152
|
+
css = Rouge::Themes.const_get(theme).render(scope: '.ex_highlight')
|
|
153
|
+
added = added.sub(/rb/, "ex")
|
|
154
|
+
css.gsub!(/{\n/, "{\n font-family: courier;")
|
|
155
|
+
css = added + "\n" + css
|
|
156
|
+
# STDERR.puts "Writing #{theme} theme to elixir.css"
|
|
157
|
+
File.write("#{dir}/elixir.css", css)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def format_ruby(source, theme = "Github", back = "black")
|
|
162
|
+
# theme/back not used now
|
|
163
|
+
formatter = Rouge::Formatters::HTML.new
|
|
164
|
+
lexer = Rouge::Lexers::Ruby.new
|
|
165
|
+
body = formatter.format(lexer.lex(source))
|
|
166
|
+
text = "<div class=rb_highlight>#{body}</div>"
|
|
167
|
+
text
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def format_elixir(source, theme = "Github", back = "black")
|
|
171
|
+
# theme/back not used now
|
|
172
|
+
formatter = Rouge::Formatters::HTML.new
|
|
173
|
+
lexer = Rouge::Lexers::Elixir.new
|
|
174
|
+
body = formatter.format(lexer.lex(source))
|
|
175
|
+
text = "<div class=ex_highlight>#{body}</div>"
|
|
176
|
+
text
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def ruby
|
|
180
|
+
file = @_args.first
|
|
181
|
+
if file.nil?
|
|
182
|
+
code = " # Ruby code\n\n"
|
|
183
|
+
_body {|line| code << " " + line + "\n" }
|
|
184
|
+
else
|
|
185
|
+
code = "# Ruby code\n\n" + ::File.read(file)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
html = format_ruby(code)
|
|
189
|
+
_out html
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def elixir
|
|
193
|
+
file = @_args.first
|
|
194
|
+
if file.nil?
|
|
195
|
+
code = ""
|
|
196
|
+
_body {|line| code << " " + line + "\n" }
|
|
197
|
+
else
|
|
198
|
+
code = ::File.read(file)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
html = format_elixir(code)
|
|
202
|
+
_out html
|
|
203
|
+
end
|
|
204
|
+
end
|
data/imports/tutorial.rb
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# require 'cgi'
|
|
2
|
+
|
|
3
|
+
require 'helpers'
|
|
4
|
+
|
|
5
|
+
module Tutorial
|
|
6
|
+
include Helpers
|
|
7
|
+
|
|
8
|
+
def title(args = nil, body = nil)
|
|
9
|
+
h1
|
|
10
|
+
_optional_blank_line
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def section(args = nil, body = nil)
|
|
14
|
+
h3
|
|
15
|
+
_optional_blank_line
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def code(args = nil, body = nil)
|
|
19
|
+
first = true # dumb hack! fixes blank space
|
|
20
|
+
_body do |line|
|
|
21
|
+
tag, first = "<pre>", false if first
|
|
22
|
+
_out "#{tag} #{escape_html(line)}" # indentation
|
|
23
|
+
end
|
|
24
|
+
_out "</pre>"
|
|
25
|
+
_optional_blank_line
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def rx(str)
|
|
29
|
+
::Regexp.compile(::Regexp.escape(str))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def inout(args = nil, body = nil)
|
|
33
|
+
src, out = _args
|
|
34
|
+
t1 = ::File.readlines(src) rescue (abort "t1 = #{src}")
|
|
35
|
+
t2 = ::File.readlines(out) rescue (abort "t2 = #{out}")
|
|
36
|
+
# To pacify markdown for README (FIXME later)
|
|
37
|
+
t1 = t1.map {|x| " " + x.sub(/ +$/,"").gsub(/_/, "\\_") }.join
|
|
38
|
+
t2 = t2.map {|x| " " + x.sub(/ +$/,"").gsub(/_/, "\\_") }.join
|
|
39
|
+
|
|
40
|
+
_out <<-HTML
|
|
41
|
+
<table width=80% cellpadding=4>
|
|
42
|
+
<tr>
|
|
43
|
+
<td width=50%><b>Input</b></td>
|
|
44
|
+
<td width=50%><b>Output</b></td>
|
|
45
|
+
</tr>
|
|
46
|
+
<tr>
|
|
47
|
+
<td width=50% bgcolor=#fee0fe valign=top>
|
|
48
|
+
<pre>#{t1}</pre>
|
|
49
|
+
</td>
|
|
50
|
+
<td width=50% bgcolor=#eeeeee valign=top>
|
|
51
|
+
<pre>#{t2}</pre>
|
|
52
|
+
</td>
|
|
53
|
+
</tr>
|
|
54
|
+
</table>
|
|
55
|
+
HTML
|
|
56
|
+
_optional_blank_line
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def put_table(src, exp)
|
|
60
|
+
t1 = ::File.readlines(src) rescue (abort "t1 = #{src}")
|
|
61
|
+
t2 = ::File.readlines(exp) rescue (abort "t2 = #{out}")
|
|
62
|
+
t1 = t1.map {|x| " " + x.sub(/ +$/,"").gsub(/_/, "\\_") }.join
|
|
63
|
+
t2 = t2.map {|x| " " + x.sub(/ +$/,"").gsub(/_/, "\\_") }.join
|
|
64
|
+
|
|
65
|
+
_out <<-HTML
|
|
66
|
+
<font size=+1>
|
|
67
|
+
<table width=80% cellpadding=4>
|
|
68
|
+
<tr>
|
|
69
|
+
<td width=50%><b>Input</b></td>
|
|
70
|
+
<td width=50%><b>Output</b></td>
|
|
71
|
+
</tr>
|
|
72
|
+
<tr>
|
|
73
|
+
<td width=50% bgcolor=#fee0fe valign=top>
|
|
74
|
+
<pre>#{t1}</pre>
|
|
75
|
+
</td>
|
|
76
|
+
<td width=50% bgcolor=#eeeeee valign=top>
|
|
77
|
+
<pre>#{t2}</pre>
|
|
78
|
+
</td>
|
|
79
|
+
</tr>
|
|
80
|
+
</table>
|
|
81
|
+
</font>
|
|
82
|
+
HTML
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def testcase(args = nil, body = nil)
|
|
86
|
+
name = _args.first
|
|
87
|
+
_out "\n<font size=+1><b>Test: </font><font size=+2><tt>#{name}</tt></font></b></h3><br>"
|
|
88
|
+
src, exp = "test/data/#{name}/source.lt3", "test/data/#{name}/expected-output.txt"
|
|
89
|
+
@_args = [src, exp] # Better way to do this??
|
|
90
|
+
put_table(src, exp)
|
|
91
|
+
_out "<br>"
|
|
92
|
+
_optional_blank_line
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
data/lib/cmdargs.rb
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
require_relative 'livetext'
|
|
2
|
+
|
|
3
|
+
=begin
|
|
4
|
+
Weird concepts to understand here...
|
|
5
|
+
|
|
6
|
+
1. A Livetext dot-command (flush left) usually looks like:
|
|
7
|
+
.foobar
|
|
8
|
+
2. A dot-command (left-indented) usually looks like:
|
|
9
|
+
$.foobar
|
|
10
|
+
3. More generally, it may have any number of parameters (0, 1, ...)
|
|
11
|
+
.redirect somefile.txt append
|
|
12
|
+
4. Variables and functions *can* appear (rare in practice??)
|
|
13
|
+
.redirect somefile$my_suffix $$my_mode
|
|
14
|
+
5. A trailing # comment may appear
|
|
15
|
+
a. Stripped... saved in #raw ? #data ? #comment ? elsewhere?
|
|
16
|
+
b. NOT the "dot" as a comment!
|
|
17
|
+
6. .foobar # This here is a comment
|
|
18
|
+
7. #data accessor returns all data on the .foo line...
|
|
19
|
+
a. ...After the initial space
|
|
20
|
+
b. ...Including spaces
|
|
21
|
+
c. Including comment??
|
|
22
|
+
d. .foo This is o n l y a test.
|
|
23
|
+
# #data returns: "This is o n l y a test."
|
|
24
|
+
e. What about formatting???
|
|
25
|
+
f. What about: comments? variables? functions?
|
|
26
|
+
|
|
27
|
+
7. Some commands have NO body while others have an OPTIONAL or REQUIRED body
|
|
28
|
+
a. Assume .cmd1 definition forbids a body (then a body is an error)
|
|
29
|
+
.cmd1 # may NOT have a body
|
|
30
|
+
b. Assume .cmd2 definition PERMITS a body
|
|
31
|
+
.cmd2 # may or MAY NOT have body/.end
|
|
32
|
+
c. Assume .cmd3 definition PERMITS a body
|
|
33
|
+
.cmd3 # REQUIRES a body/.end
|
|
34
|
+
. stuff...
|
|
35
|
+
.end
|
|
36
|
+
8. Inside a body:
|
|
37
|
+
8a. Leading dot has no special meaning (though the associated method may parse it!)
|
|
38
|
+
8b. BUG? Currently leading dot is a comment INSIDE a body?
|
|
39
|
+
8a. No leading char is special (though the associated method may parse it!)
|
|
40
|
+
8a. No trailing #-comments (though the associated method may parse it!)
|
|
41
|
+
8c. ?? We should or shouldn't look for variables/functions? or make it an option?
|
|
42
|
+
8d. .end may naturally not be used (but see .raw where it may)
|
|
43
|
+
9. the args accessor is a simple array of strings
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
=end
|
|
47
|
+
|
|
48
|
+
class Livetext::CmdData
|
|
49
|
+
|
|
50
|
+
attr_reader :data, :args, :nargs, :arity, :comment, :raw # , ...?
|
|
51
|
+
|
|
52
|
+
def initialize(data, body: false, arity: :N) # FIXME maybe just add **options ??
|
|
53
|
+
# arity: (num) fixed number 0 or more
|
|
54
|
+
# :N arbitrary number
|
|
55
|
+
# n1..n2 range
|
|
56
|
+
# body: true => this command has a body + .end
|
|
57
|
+
# how raw is raw?
|
|
58
|
+
# remove comment - always/sometimes/never?
|
|
59
|
+
# var_func_parse - always/sometimes/never?
|
|
60
|
+
# var_func_parse inside body??
|
|
61
|
+
@data = data.dup # comment? vars? funcs?
|
|
62
|
+
@raw = data.dup # comment? vars? funcs?
|
|
63
|
+
@args = data.split # simple array
|
|
64
|
+
@nargs = nargs # not really "needed"
|
|
65
|
+
check_num_args(nargs)
|
|
66
|
+
|
|
67
|
+
# @varfunc = _var_func_parse(data.dup)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def check_num_args(num)
|
|
71
|
+
num_range = /(\d{0,2})(\.\.)(\d{0,2})/
|
|
72
|
+
min, max = 0, 9999
|
|
73
|
+
md = num_range.match(@nargs).to_a
|
|
74
|
+
bad_args = nil
|
|
75
|
+
case
|
|
76
|
+
when @nargs == ":N" # arbitrary
|
|
77
|
+
# max already set
|
|
78
|
+
when md[2] == ".." # range: 4..6 1.. ..4
|
|
79
|
+
vmin, vmax = md.values_at(1, 2)
|
|
80
|
+
min = Integer(vmin) unless vmin.empty?
|
|
81
|
+
max = Integer(vmax) unless vmax.empty?
|
|
82
|
+
min, max = Integer(min), Integer(max)
|
|
83
|
+
when %r[^\d+$] =~ num
|
|
84
|
+
min = max = Integer(num) # can raise error
|
|
85
|
+
else
|
|
86
|
+
raise "Invalid value or range '#{num.inspect}'"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
bad_args = @args.size.between?(min, max)
|
|
90
|
+
raise "Expected #{num} args but found #{@args.size}!" if bad_args
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
end
|
data/lib/errors.rb
CHANGED
|
@@ -3,11 +3,13 @@
|
|
|
3
3
|
|
|
4
4
|
def make_exception(sym, str, target_class = Object)
|
|
5
5
|
return if target_class.constants.include?(sym)
|
|
6
|
-
|
|
6
|
+
klass = sym # :"#{sym}_Class"
|
|
7
|
+
target_class.const_set(klass, StandardError.dup)
|
|
7
8
|
define_method(sym) do |*args|
|
|
9
|
+
args = [] unless args.first
|
|
8
10
|
msg = str.dup
|
|
9
11
|
args.each.with_index {|arg, i| msg.sub!("%#{i+1}", arg) }
|
|
10
|
-
target_class.class_eval(
|
|
12
|
+
target_class.class_eval(klass.to_s).new(msg)
|
|
11
13
|
end
|
|
12
14
|
end
|
|
13
15
|
|
data/lib/formatline.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Class FormatLine handles the parsing of comments, dot commands, and
|
|
2
2
|
# simple formatting characters.
|
|
3
3
|
|
|
4
|
-
class FormatLine
|
|
4
|
+
class FormatLine < StringParser
|
|
5
5
|
SimpleFormats = {}
|
|
6
6
|
SimpleFormats[:b] = %w[<b> </b>]
|
|
7
7
|
SimpleFormats[:i] = %w[<i> </i>]
|
|
@@ -24,20 +24,11 @@ class FormatLine
|
|
|
24
24
|
|
|
25
25
|
Syms = { "*" => :b, "_" => :i, "`" => :t, "~" => :s }
|
|
26
26
|
|
|
27
|
-
def terminate?(terminators, ch)
|
|
28
|
-
if terminators.is_a? Regexp
|
|
29
|
-
terminators === ch
|
|
30
|
-
else
|
|
31
|
-
terminators.include?(ch)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
27
|
attr_reader :out
|
|
36
28
|
attr_reader :tokenlist
|
|
37
29
|
|
|
38
30
|
def initialize(line)
|
|
39
|
-
|
|
40
|
-
@i = -1
|
|
31
|
+
super
|
|
41
32
|
@token = Null.dup
|
|
42
33
|
@tokenlist = []
|
|
43
34
|
end
|
|
@@ -45,26 +36,27 @@ class FormatLine
|
|
|
45
36
|
def self.parse!(line)
|
|
46
37
|
return nil if line.nil?
|
|
47
38
|
x = self.new(line.chomp)
|
|
48
|
-
t = x.tokenize
|
|
39
|
+
t = x.tokenize
|
|
40
|
+
# TTY.puts "tokens = \n#{t.inspect}\n "
|
|
49
41
|
x.evaluate
|
|
50
42
|
end
|
|
51
43
|
|
|
52
|
-
def tokenize
|
|
53
|
-
|
|
44
|
+
def tokenize
|
|
45
|
+
# add grab
|
|
54
46
|
loop do
|
|
55
|
-
case
|
|
56
|
-
when Escape; grab; add
|
|
47
|
+
case peek
|
|
48
|
+
when Escape; grab; add peek; grab; add peek
|
|
57
49
|
when "$"
|
|
58
50
|
dollar
|
|
59
51
|
when "*", "_", "`", "~"
|
|
60
|
-
marker
|
|
61
|
-
add
|
|
52
|
+
marker peek
|
|
53
|
+
add peek
|
|
62
54
|
when LF
|
|
63
55
|
break if @i >= line.size - 1
|
|
64
56
|
when nil
|
|
65
57
|
break
|
|
66
58
|
else
|
|
67
|
-
add
|
|
59
|
+
add peek
|
|
68
60
|
end
|
|
69
61
|
grab
|
|
70
62
|
end
|
|
@@ -72,24 +64,33 @@ class FormatLine
|
|
|
72
64
|
@tokenlist
|
|
73
65
|
end
|
|
74
66
|
|
|
67
|
+
def terminate?(terminators, ch)
|
|
68
|
+
if terminators.is_a? Regexp
|
|
69
|
+
terminators === ch
|
|
70
|
+
else
|
|
71
|
+
terminators.include?(ch)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
75
|
def self.var_func_parse(str)
|
|
76
76
|
return nil if str.nil?
|
|
77
77
|
x = self.new(str.chomp)
|
|
78
|
-
x.
|
|
79
|
-
loop do
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
break
|
|
86
|
-
else
|
|
87
|
-
x.add x.curr
|
|
88
|
-
end
|
|
89
|
-
x.grab
|
|
78
|
+
char = x.peek
|
|
79
|
+
loop do
|
|
80
|
+
char = x.grab
|
|
81
|
+
break if char == LF || char == nil
|
|
82
|
+
x.handle_escaping if char == Escape
|
|
83
|
+
x.dollar if char == "$"
|
|
84
|
+
x.add char
|
|
90
85
|
end
|
|
91
86
|
x.add_token(:str)
|
|
92
|
-
x.evaluate
|
|
87
|
+
result = x.evaluate
|
|
88
|
+
result
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def handle_escaping
|
|
92
|
+
grab
|
|
93
|
+
add grab
|
|
93
94
|
end
|
|
94
95
|
|
|
95
96
|
def embed(sym, str)
|
|
@@ -130,27 +131,6 @@ class FormatLine
|
|
|
130
131
|
@out
|
|
131
132
|
end
|
|
132
133
|
|
|
133
|
-
def curr
|
|
134
|
-
@line[@i]
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def prev
|
|
138
|
-
return nil if @i <= 0
|
|
139
|
-
@line[@i-1]
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def next!
|
|
143
|
-
@line[@i+1]
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
def grab
|
|
147
|
-
@line[@i+=1]
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def ungrab
|
|
151
|
-
@line[@i-=1]
|
|
152
|
-
end
|
|
153
|
-
|
|
154
134
|
def grab_colon_param
|
|
155
135
|
grab # grab :
|
|
156
136
|
param = ""
|
|
@@ -187,7 +167,7 @@ class FormatLine
|
|
|
187
167
|
end
|
|
188
168
|
end
|
|
189
169
|
|
|
190
|
-
add
|
|
170
|
+
add peek
|
|
191
171
|
grab
|
|
192
172
|
param = nil if param.empty?
|
|
193
173
|
param
|
|
@@ -207,8 +187,8 @@ class FormatLine
|
|
|
207
187
|
str = Null.dup
|
|
208
188
|
grab
|
|
209
189
|
loop do
|
|
210
|
-
break if
|
|
211
|
-
str <<
|
|
190
|
+
break if eos?
|
|
191
|
+
str << peek
|
|
212
192
|
break if terminate?(NoAlpha, next!)
|
|
213
193
|
grab
|
|
214
194
|
end
|
|
@@ -219,8 +199,8 @@ class FormatLine
|
|
|
219
199
|
str = Null.dup
|
|
220
200
|
grab
|
|
221
201
|
loop do
|
|
222
|
-
break if
|
|
223
|
-
str <<
|
|
202
|
+
break if eos? # peek.nil?
|
|
203
|
+
str << peek
|
|
224
204
|
break if terminate?(NoAlphaDot, next!)
|
|
225
205
|
grab
|
|
226
206
|
end
|
|
@@ -229,7 +209,7 @@ class FormatLine
|
|
|
229
209
|
|
|
230
210
|
def dollar
|
|
231
211
|
grab
|
|
232
|
-
case
|
|
212
|
+
case peek
|
|
233
213
|
when LF; add "$"; add_token :str
|
|
234
214
|
when " "; add "$ "; add_token :str
|
|
235
215
|
when nil; add "$"; add_token :str
|
|
@@ -237,10 +217,10 @@ class FormatLine
|
|
|
237
217
|
# when "."; dollar_dot
|
|
238
218
|
when /[A-Za-z]/
|
|
239
219
|
add_token :str
|
|
240
|
-
var =
|
|
220
|
+
var = peek + grab_alpha_dot
|
|
241
221
|
add_token(:var, var)
|
|
242
222
|
else
|
|
243
|
-
add "$" +
|
|
223
|
+
add "$" + peek
|
|
244
224
|
add_token(:string)
|
|
245
225
|
end
|
|
246
226
|
end
|
|
@@ -258,7 +238,7 @@ class FormatLine
|
|
|
258
238
|
when "["; param = grab_func_param; add_token(:brackets, param)
|
|
259
239
|
end
|
|
260
240
|
else
|
|
261
|
-
grab; add_token :str, "$$" +
|
|
241
|
+
grab; add_token :str, "$$" + peek; return
|
|
262
242
|
end
|
|
263
243
|
end
|
|
264
244
|
|
|
@@ -275,7 +255,7 @@ class FormatLine
|
|
|
275
255
|
end
|
|
276
256
|
|
|
277
257
|
grab
|
|
278
|
-
case
|
|
258
|
+
case peek
|
|
279
259
|
when Space
|
|
280
260
|
add char + " "
|
|
281
261
|
add_token :str
|
|
@@ -286,7 +266,7 @@ class FormatLine
|
|
|
286
266
|
when char; double_marker(char)
|
|
287
267
|
when LBrack; long_marker(char)
|
|
288
268
|
else
|
|
289
|
-
str =
|
|
269
|
+
str = peek + collect!(sym, Blank)
|
|
290
270
|
add str
|
|
291
271
|
add_token sym, str
|
|
292
272
|
grab
|
|
@@ -319,21 +299,19 @@ class FormatLine
|
|
|
319
299
|
str = Null.dup # next is not " ","*","["
|
|
320
300
|
grab # ZZZ
|
|
321
301
|
loop do
|
|
322
|
-
if
|
|
323
|
-
str << grab # ch = escaped char
|
|
302
|
+
if peek == Escape
|
|
324
303
|
grab
|
|
304
|
+
str << grab
|
|
325
305
|
next
|
|
326
306
|
end
|
|
327
|
-
if terminate?(terminators,
|
|
307
|
+
if terminate?(terminators, peek)
|
|
328
308
|
break
|
|
329
309
|
end
|
|
330
|
-
|
|
331
|
-
str << curr # not a terminator
|
|
310
|
+
str << peek # not a terminator
|
|
332
311
|
grab
|
|
333
|
-
# STDERR.puts "After grab, curr is #{curr.inspect}"
|
|
334
312
|
end
|
|
335
313
|
|
|
336
|
-
if
|
|
314
|
+
if peek == "]" # skip right bracket
|
|
337
315
|
grab
|
|
338
316
|
end
|
|
339
317
|
add str
|
|
@@ -344,8 +322,8 @@ class FormatLine
|
|
|
344
322
|
end
|
|
345
323
|
|
|
346
324
|
def escaped
|
|
347
|
-
ch = grab
|
|
348
325
|
grab
|
|
326
|
+
ch = grab
|
|
349
327
|
ch
|
|
350
328
|
end
|
|
351
329
|
|
|
@@ -356,15 +334,15 @@ class FormatLine
|
|
|
356
334
|
grab # ZZZ
|
|
357
335
|
loop do
|
|
358
336
|
case
|
|
359
|
-
when
|
|
337
|
+
when peek.nil?
|
|
360
338
|
return str
|
|
361
|
-
when
|
|
339
|
+
when peek == Escape
|
|
362
340
|
str << escaped
|
|
363
341
|
next
|
|
364
|
-
when terminate?(terminators,
|
|
342
|
+
when terminate?(terminators, peek)
|
|
365
343
|
break
|
|
366
344
|
else
|
|
367
|
-
str <<
|
|
345
|
+
str << peek # not a terminator
|
|
368
346
|
end
|
|
369
347
|
grab
|
|
370
348
|
end
|
|
@@ -376,17 +354,14 @@ class FormatLine
|
|
|
376
354
|
STDERR.puts "=== str = #{str.inspect}"
|
|
377
355
|
end
|
|
378
356
|
|
|
379
|
-
############
|
|
380
|
-
|
|
381
|
-
### From FormatLine:
|
|
382
|
-
|
|
383
357
|
def funcall(name, param)
|
|
358
|
+
err = "[Error evaluating $$#{name}(#{param})]"
|
|
384
359
|
result =
|
|
385
360
|
if self.respond_to?("func_" + name.to_s)
|
|
386
361
|
self.send("func_" + name.to_s, param)
|
|
387
362
|
else
|
|
388
363
|
fobj = ::Livetext::Functions.new
|
|
389
|
-
fobj.send(name, param)
|
|
364
|
+
fobj.send(name, param) rescue err
|
|
390
365
|
end
|
|
391
366
|
result
|
|
392
367
|
end
|
|
@@ -396,8 +371,6 @@ class FormatLine
|
|
|
396
371
|
result
|
|
397
372
|
end
|
|
398
373
|
|
|
399
|
-
#####
|
|
400
|
-
|
|
401
374
|
def embedded?
|
|
402
375
|
! (['"', "'", " ", nil].include? prev)
|
|
403
376
|
end
|