livetext 0.9.15 → 0.9.21
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 +4 -2
- data/README.md +330 -339
- data/bin/livetext +0 -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 +45 -0
- data/imports/pyggish.rb +204 -0
- data/imports/tutorial.rb +95 -0
- data/lib/cmdargs.rb +26 -17
- data/lib/formatline.rb +4 -4
- data/lib/handler/icanhaz.rb +35 -0
- data/lib/handler.rb +1 -0
- data/lib/helpers.rb +59 -2
- data/lib/livetext.rb +6 -1
- data/lib/parser/general.rb +1 -1
- data/lib/parser/import.rb +1 -3
- data/lib/parser/mixin.rb +1 -16
- data/lib/parser/set.rb +2 -2
- data/lib/parser/string.rb +9 -8
- data/lib/processor.rb +19 -11
- data/lib/standard.rb +13 -2
- data/lib/userapi.rb +3 -2
- data/livetext.gemspec +2 -1
- data/plugin/markdown.rb +6 -6
- data/plugin/tutorial.rb +4 -3
- 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/icanhaz/source.lt3 +10 -0
- 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/subset.txt +84 -0
- data/test/snapshots.rb +71 -37
- data/test/unit/parser/set.rb +17 -15
- data/test/unit/parser/string.rb +45 -6
- metadata +19 -33
- data/test/affirm/kbks.jpg +0 -0
- data/test/affirm/lm-kbks.lt +0 -19
- data/test/cleanup +0 -1
- data/test/newtest +0 -14
- data/test/sdtest +0 -6
- data/test/snapshots/OMIT.txt +0 -9
- data/test/snapshots/clusion.txt +0 -35
- data/test/snapshots/crap +0 -16
- data/test/snapshots/error_inc_line_num/actual-error.txt +0 -14
- data/test/snapshots/error_inc_line_num/actual-output.txt +0 -7
- 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/out-sdiff.txt +0 -6
- data/test/snapshots/error_missing_end/actual-error.txt +0 -10
- data/test/snapshots/error_missing_end/actual-output.txt +0 -0
- data/test/snapshots/error_missing_end/out-sdiff.txt +0 -6
- data/test/snapshots/error_no_such_copy/actual-error.txt +0 -10
- data/test/snapshots/error_no_such_copy/actual-output.txt +0 -0
- 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/out-sdiff.txt +0 -6
- data/test/snapshots/error_no_such_mixin/actual-error.txt +0 -37
- data/test/snapshots/error_no_such_mixin/actual-output.txt +0 -0
- data/test/snapshots/error_no_such_mixin/out-sdiff.txt +0 -6
- data/test/snapshots/fixit +0 -6
- data/test/snapshots/simple_import/actual-error.txt +0 -8
- data/test/snapshots/simple_import/actual-output.txt +0 -3
- data/test/snapshots/simple_import/err-sdiff.txt +0 -9
- data/test/snapshots/simple_import/out-sdiff.txt +0 -9
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/snapshots/#{name}/source.lt3", "test/snapshots/#{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
CHANGED
|
@@ -4,44 +4,53 @@ require_relative 'livetext'
|
|
|
4
4
|
Weird concepts to understand here...
|
|
5
5
|
|
|
6
6
|
1. A Livetext dot-command (flush left) usually looks like:
|
|
7
|
-
|
|
7
|
+
.foobar
|
|
8
|
+
|
|
8
9
|
2. A dot-command (left-indented) usually looks like:
|
|
9
|
-
|
|
10
|
+
$.foobar
|
|
11
|
+
|
|
10
12
|
3. More generally, it may have any number of parameters (0, 1, ...)
|
|
11
|
-
|
|
13
|
+
.redirect somefile.txt append
|
|
14
|
+
|
|
12
15
|
4. Variables and functions *can* appear (rare in practice??)
|
|
13
|
-
|
|
16
|
+
.redirect somefile$my_suffix $$my_mode
|
|
17
|
+
|
|
14
18
|
5. A trailing # comment may appear
|
|
15
19
|
a. Stripped... saved in #raw ? #data ? #comment ? elsewhere?
|
|
16
20
|
b. NOT the "dot" as a comment!
|
|
21
|
+
|
|
17
22
|
6. .foobar # This here is a comment
|
|
23
|
+
|
|
18
24
|
7. #data accessor returns all data on the .foo line...
|
|
19
25
|
a. ...After the initial space
|
|
20
|
-
b. ...Including spaces
|
|
26
|
+
b. ...Including later spaces
|
|
21
27
|
c. Including comment??
|
|
22
28
|
d. .foo This is o n l y a test.
|
|
23
29
|
# #data returns: "This is o n l y a test."
|
|
24
30
|
e. What about formatting???
|
|
25
31
|
f. What about: comments? variables? functions?
|
|
26
32
|
|
|
27
|
-
|
|
33
|
+
8. Some commands have NO body while others have an OPTIONAL or REQUIRED body
|
|
28
34
|
a. Assume .cmd1 definition forbids a body (then a body is an error)
|
|
29
35
|
.cmd1 # may NOT have a body
|
|
30
36
|
b. Assume .cmd2 definition PERMITS a body
|
|
31
37
|
.cmd2 # may or MAY NOT have body/.end
|
|
32
|
-
c. Assume .cmd3 definition
|
|
38
|
+
c. Assume .cmd3 definition REQUIRES a body
|
|
33
39
|
.cmd3 # REQUIRES a body/.end
|
|
34
40
|
. stuff...
|
|
35
41
|
.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
42
|
|
|
43
|
+
9. Inside a body:
|
|
44
|
+
a. Leading dot has no special meaning (though the associated method may parse it!)
|
|
45
|
+
b. BUG? Currently leading dot is a comment INSIDE a body?
|
|
46
|
+
c. No leading char is special (though the associated method may parse it!)
|
|
47
|
+
d. No trailing #-comments (though the associated method may parse it!)
|
|
48
|
+
e. ?? We should or shouldn't look for variables/functions? or make it an option?
|
|
49
|
+
f. .end may naturally not be used (but see .raw where it may)
|
|
50
|
+
|
|
51
|
+
10. The args accessor is a simple array of strings
|
|
52
|
+
a. there is also raw_args (without variables/functions, etc.)
|
|
53
|
+
b. Much of this HAS NOT been thought through yet!
|
|
45
54
|
|
|
46
55
|
=end
|
|
47
56
|
|
|
@@ -64,11 +73,11 @@ class Livetext::CmdData
|
|
|
64
73
|
@nargs = nargs # not really "needed"
|
|
65
74
|
check_num_args(nargs)
|
|
66
75
|
|
|
67
|
-
# @varfunc =
|
|
76
|
+
# @varfunc = Livetext.interpolate(data.dup)
|
|
68
77
|
end
|
|
69
78
|
|
|
70
79
|
def check_num_args(num)
|
|
71
|
-
num_range = /(\d{0,2})(\.\.)(\d{0,2})/
|
|
80
|
+
num_range = /(\d{0,2})(\.\.)(\d{0,2})/ # Not "really" right...
|
|
72
81
|
min, max = 0, 9999
|
|
73
82
|
md = num_range.match(@nargs).to_a
|
|
74
83
|
bad_args = nil
|
data/lib/formatline.rb
CHANGED
|
@@ -52,7 +52,7 @@ class FormatLine < StringParser
|
|
|
52
52
|
marker peek
|
|
53
53
|
add peek
|
|
54
54
|
when LF
|
|
55
|
-
break if @i >= line.size - 1
|
|
55
|
+
break if eos? # @i >= line.size - 1
|
|
56
56
|
when nil
|
|
57
57
|
break
|
|
58
58
|
else
|
|
@@ -117,11 +117,11 @@ class FormatLine < StringParser
|
|
|
117
117
|
if [:colon, :brackets].include? arg[0]
|
|
118
118
|
arg = gen.next # for real
|
|
119
119
|
param = arg[1]
|
|
120
|
-
param =
|
|
120
|
+
param = Livetext.interpolate(param)
|
|
121
121
|
end
|
|
122
122
|
@out << funcall(val, param)
|
|
123
123
|
when :b, :i, :t, :s
|
|
124
|
-
val =
|
|
124
|
+
val = Livetext.interpolate(val)
|
|
125
125
|
@out << embed(sym, val)
|
|
126
126
|
else
|
|
127
127
|
add_token :str
|
|
@@ -199,7 +199,7 @@ class FormatLine < StringParser
|
|
|
199
199
|
str = Null.dup
|
|
200
200
|
grab
|
|
201
201
|
loop do
|
|
202
|
-
break if
|
|
202
|
+
break if peek.nil? # eos?
|
|
203
203
|
str << peek
|
|
204
204
|
break if terminate?(NoAlphaDot, next!)
|
|
205
205
|
grab
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
|
|
2
|
+
class Livetext::Handler::ICanHaz
|
|
3
|
+
include Helpers
|
|
4
|
+
|
|
5
|
+
attr_reader :file
|
|
6
|
+
|
|
7
|
+
def initialize(name)
|
|
8
|
+
@name = name
|
|
9
|
+
@file = find_file(name)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.get_module(name)
|
|
13
|
+
handler = self.new(name)
|
|
14
|
+
const1 = Object.constants
|
|
15
|
+
@file = handler.file.sub(/.rb$/, "")
|
|
16
|
+
require @file # + ".rb"
|
|
17
|
+
const2 = Object.constants
|
|
18
|
+
names = (const2 - const1)
|
|
19
|
+
abort "Expected ONE new constant: #{names.inspect}" if names.size != 1
|
|
20
|
+
modname = names.first.to_s
|
|
21
|
+
newmod = Object.const_get("::" + modname)
|
|
22
|
+
newmod # return actual module
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def cwd_root?
|
|
28
|
+
File.dirname(File.expand_path(".")) == "/"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def fname2module(name)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
|
data/lib/handler.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require_relative 'handler/icanhaz'
|
data/lib/helpers.rb
CHANGED
|
@@ -4,6 +4,40 @@ module Helpers
|
|
|
4
4
|
Space = " "
|
|
5
5
|
Sigil = "." # Can't change yet
|
|
6
6
|
|
|
7
|
+
ESCAPING = { "'" => ''', '&' => '&', '"' => '"',
|
|
8
|
+
'<' => '<', '>' => '>' }
|
|
9
|
+
|
|
10
|
+
def escape_html(string)
|
|
11
|
+
enc = string.encoding
|
|
12
|
+
unless enc.ascii_compatible?
|
|
13
|
+
if enc.dummy?
|
|
14
|
+
origenc = enc
|
|
15
|
+
enc = Encoding::Converter.asciicompat_encoding(enc)
|
|
16
|
+
string = enc ? string.encode(enc) : string.b
|
|
17
|
+
end
|
|
18
|
+
table = Hash[ESCAPING.map {|pair|pair.map {|s|s.encode(enc)}}]
|
|
19
|
+
string = string.gsub(/#{"['&\"<>]".encode(enc)}/, table)
|
|
20
|
+
string.encode!(origenc) if origenc
|
|
21
|
+
return string
|
|
22
|
+
end
|
|
23
|
+
string.gsub(/['&\"<>]/, ESCAPING)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def find_file(name, ext=".rb")
|
|
27
|
+
paths = [Livetext::Path.sub(/lib/, "imports/"), "./"]
|
|
28
|
+
base = "#{name}#{ext}"
|
|
29
|
+
paths.each do |path|
|
|
30
|
+
file = path + base
|
|
31
|
+
return file if File.exist?(file)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
raise "No such mixin '#{name}'"
|
|
35
|
+
|
|
36
|
+
# # Really want to search upward??
|
|
37
|
+
# raise "No such mixin '#{name}'" if cwd_root?
|
|
38
|
+
# Dir.chdir("..") { find_file(name) }
|
|
39
|
+
end
|
|
40
|
+
|
|
7
41
|
def self.rx(str, space=nil)
|
|
8
42
|
Regexp.compile("^" + Regexp.escape(str) + "#{space}")
|
|
9
43
|
end
|
|
@@ -30,7 +64,7 @@ module Helpers
|
|
|
30
64
|
@body
|
|
31
65
|
end
|
|
32
66
|
|
|
33
|
-
def process_line(line)
|
|
67
|
+
def process_line(line)
|
|
34
68
|
nomarkup = true
|
|
35
69
|
case line # must apply these in order
|
|
36
70
|
when Comment
|
|
@@ -59,6 +93,29 @@ module Helpers
|
|
|
59
93
|
raise EndWithoutOpening()
|
|
60
94
|
when @main.respond_to?(name)
|
|
61
95
|
result = @main.send(name)
|
|
96
|
+
|
|
97
|
+
# NOTE: The above line is where the magic happens!
|
|
98
|
+
# A name like 'foobar' results in an invocation of
|
|
99
|
+
# @main.foobar (where @main is a Processor, and any
|
|
100
|
+
# new methods (e.g. from a mixin) are added to @main
|
|
101
|
+
#
|
|
102
|
+
# So all the functionality from _args and _raw_args
|
|
103
|
+
# and _data (among others?) will be encapsulated in
|
|
104
|
+
# 'some' kind of PORO which handles access to all
|
|
105
|
+
# these things as well as the 'body' between the
|
|
106
|
+
# command and its corresponding .end
|
|
107
|
+
#
|
|
108
|
+
# The 'body' functionality is so commonly used, I plan
|
|
109
|
+
# to pass it in separately as needed (even though the
|
|
110
|
+
# args object should make it available also).
|
|
111
|
+
#
|
|
112
|
+
# Every method corresponding to a dot commmand will
|
|
113
|
+
# get args and body passed in as needed. Every one of
|
|
114
|
+
# the signatures already has (args = nil, body = nil)
|
|
115
|
+
# but nothing is being passed in that way yet.
|
|
116
|
+
#
|
|
117
|
+
# Refer to lib/cmdargs.rb for more! This is *strictly*
|
|
118
|
+
# experimental and a "work in progress."
|
|
62
119
|
else
|
|
63
120
|
puts @body # earlier correct output, not flushed yet
|
|
64
121
|
raise "Name '#{name}' is unknown"
|
|
@@ -74,8 +131,8 @@ module Helpers
|
|
|
74
131
|
name, data = line.split(" ", 2)
|
|
75
132
|
name = name[1..-1] # chop off sigil
|
|
76
133
|
name = "dot_" + name if %w[include def].include?(name)
|
|
77
|
-
@main.data = data
|
|
78
134
|
@main.check_disallowed(name)
|
|
135
|
+
@main.data = data
|
|
79
136
|
name
|
|
80
137
|
end
|
|
81
138
|
|
data/lib/livetext.rb
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
# Class Livetext skeleton (top level).
|
|
2
2
|
|
|
3
3
|
class Livetext
|
|
4
|
-
VERSION = "0.9.
|
|
4
|
+
VERSION = "0.9.21"
|
|
5
5
|
Path = File.expand_path(File.join(File.dirname(__FILE__)))
|
|
6
|
+
|
|
7
|
+
module Handler
|
|
8
|
+
end
|
|
6
9
|
end
|
|
7
10
|
|
|
8
11
|
# $LOAD_PATH << Livetext::Path
|
|
@@ -16,8 +19,10 @@ require_relative 'standard'
|
|
|
16
19
|
require_relative 'formatline'
|
|
17
20
|
require_relative 'processor'
|
|
18
21
|
require_relative 'helpers'
|
|
22
|
+
require_relative 'handler'
|
|
19
23
|
|
|
20
24
|
Plugins = File.expand_path(File.join(File.dirname(__FILE__), "../plugin"))
|
|
25
|
+
Imports = File.expand_path(File.join(File.dirname(__FILE__), "../imports"))
|
|
21
26
|
|
|
22
27
|
TTY = ::File.open("/dev/tty", "w")
|
|
23
28
|
|
data/lib/parser/general.rb
CHANGED
|
@@ -27,7 +27,7 @@ class Livetext::ParseGeneral < StringParser
|
|
|
27
27
|
lines.each do |line|
|
|
28
28
|
next if line.strip.empty?
|
|
29
29
|
var, value = line.split(" ", 2)
|
|
30
|
-
val =
|
|
30
|
+
val = Livetext.interpolate(value)
|
|
31
31
|
var = prefix + "." + var if prefix
|
|
32
32
|
pairs << [var, value]
|
|
33
33
|
end
|
data/lib/parser/import.rb
CHANGED
|
@@ -4,9 +4,7 @@ require '../livetext/importable'
|
|
|
4
4
|
make_exception(:BadVariableName, "Error: invalid variable name")
|
|
5
5
|
make_exception(:NoEqualSign, "Error: no equal sign found")
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class Livetext::ParseImport
|
|
7
|
+
class Livetext::Handler::Import
|
|
10
8
|
def use_import(name)
|
|
11
9
|
require name
|
|
12
10
|
include name
|
data/lib/parser/mixin.rb
CHANGED
|
@@ -2,13 +2,9 @@ require_relative '../livetext'
|
|
|
2
2
|
require_relative '../helpers'
|
|
3
3
|
require_relative 'string'
|
|
4
4
|
|
|
5
|
-
make_exception(:BadVariableName, "Error: invalid variable name")
|
|
6
5
|
make_exception(:NoEqualSign, "Error: no equal sign found")
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class Livetext::ParseMixin # < StringParser
|
|
11
|
-
|
|
7
|
+
class Livetext::ParseMixin
|
|
12
8
|
include Helpers
|
|
13
9
|
|
|
14
10
|
def initialize(name)
|
|
@@ -37,17 +33,6 @@ class Livetext::ParseMixin # < StringParser
|
|
|
37
33
|
File.dirname(File.expand_path(".")) == "/"
|
|
38
34
|
end
|
|
39
35
|
|
|
40
|
-
def find_file(name, ext=".rb")
|
|
41
|
-
base = "./#{name}#{ext}"
|
|
42
|
-
file = "#{Plugins}/#{base}"
|
|
43
|
-
return file if File.exist?(file)
|
|
44
|
-
|
|
45
|
-
file = base
|
|
46
|
-
return file if File.exist?(file)
|
|
47
|
-
|
|
48
|
-
raise "No such mixin '#{name}'" if cwd_root?
|
|
49
|
-
Dir.chdir("..") { find_file(name) }
|
|
50
|
-
end
|
|
51
36
|
|
|
52
37
|
end
|
|
53
38
|
|
data/lib/parser/set.rb
CHANGED
|
@@ -31,8 +31,8 @@ class Livetext::ParseSet < StringParser
|
|
|
31
31
|
pairs = []
|
|
32
32
|
char = nil
|
|
33
33
|
loop do
|
|
34
|
-
break if eos? # end of string
|
|
35
34
|
char = skip_spaces
|
|
35
|
+
break if eos? # end of string
|
|
36
36
|
raise "Expected alpha to start var name" unless char =~ /[a-z]/i
|
|
37
37
|
pairs << assignment
|
|
38
38
|
char = skip_spaces
|
|
@@ -56,7 +56,7 @@ class Livetext::ParseSet < StringParser
|
|
|
56
56
|
var = get_var
|
|
57
57
|
skip_equal
|
|
58
58
|
value = get_value
|
|
59
|
-
value =
|
|
59
|
+
value = Livetext.interpolate(value)
|
|
60
60
|
pair = [var, value]
|
|
61
61
|
pair
|
|
62
62
|
end
|
data/lib/parser/string.rb
CHANGED
|
@@ -15,12 +15,13 @@ class StringParser
|
|
|
15
15
|
return nil if @eos
|
|
16
16
|
char = @line[@i]
|
|
17
17
|
@i += 1
|
|
18
|
-
|
|
18
|
+
check_eos
|
|
19
19
|
char
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def ungrab
|
|
23
|
-
@i -= 1
|
|
23
|
+
@i -= 1
|
|
24
|
+
check_eos
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
def next!
|
|
@@ -32,12 +33,7 @@ class StringParser
|
|
|
32
33
|
@line[@i-1]
|
|
33
34
|
end
|
|
34
35
|
|
|
35
|
-
def last?
|
|
36
|
-
@i > @len - 1
|
|
37
|
-
end
|
|
38
|
-
|
|
39
36
|
def eos?
|
|
40
|
-
@eos = true if last? # duh?
|
|
41
37
|
@eos
|
|
42
38
|
end
|
|
43
39
|
|
|
@@ -52,11 +48,16 @@ class StringParser
|
|
|
52
48
|
char = peek
|
|
53
49
|
break if eos?
|
|
54
50
|
break if char != " "
|
|
55
|
-
grab
|
|
51
|
+
char = grab
|
|
56
52
|
end
|
|
57
53
|
char
|
|
58
54
|
end
|
|
59
55
|
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def check_eos
|
|
59
|
+
@eos = @i >= @len
|
|
60
|
+
end
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
=begin
|