livetext 0.9.14 → 0.9.20

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.lt3 +2 -2
  3. data/imports/bookish.rb +288 -0
  4. data/imports/calibre.rb +28 -0
  5. data/imports/livemagick.rb +133 -0
  6. data/imports/markdown.rb +44 -0
  7. data/imports/markdown_importable.rb +46 -0
  8. data/imports/pyggish.rb +204 -0
  9. data/imports/tutorial.rb +95 -0
  10. data/lib/cmdargs.rb +93 -0
  11. data/lib/formatline.rb +56 -83
  12. data/lib/handler/icanhaz.rb +35 -0
  13. data/lib/handler.rb +1 -0
  14. data/lib/helpers.rb +173 -4
  15. data/lib/livetext.rb +16 -141
  16. data/lib/parser/file.rb +8 -0
  17. data/lib/parser/import.rb +1 -3
  18. data/lib/parser/mixin.rb +22 -24
  19. data/lib/parser/set.rb +35 -26
  20. data/lib/parser/string.rb +19 -4
  21. data/lib/processor.rb +1 -4
  22. data/lib/standard.rb +68 -97
  23. data/lib/userapi.rb +1 -0
  24. data/livetext.gemspec +2 -1
  25. data/plugin/bookish.rb +26 -22
  26. data/plugin/calibre.rb +1 -1
  27. data/plugin/livemagick.rb +10 -10
  28. data/plugin/markdown.rb +13 -11
  29. data/plugin/pyggish.rb +94 -84
  30. data/plugin/tutorial.rb +10 -5
  31. data/test/all.rb +0 -1
  32. data/test/snapshots/OMIT.txt +9 -8
  33. data/test/snapshots/clusion.txt +84 -0
  34. data/test/snapshots/error_inc_line_num/match-error.txt +1 -1
  35. data/test/snapshots/error_invalid_name/match-error.txt +1 -1
  36. data/test/snapshots/error_line_num/match-error.txt +1 -1
  37. data/test/snapshots/error_mismatched_end/match-error.txt +1 -1
  38. data/test/snapshots/error_missing_end/match-error.txt +1 -1
  39. data/test/snapshots/error_no_such_copy/match-error.txt +1 -1
  40. data/test/snapshots/error_no_such_copy/source.lt3 +0 -1
  41. data/test/snapshots/error_no_such_inc/match-error.txt +1 -1
  42. data/test/snapshots/icanhaz/expected-output.txt +5 -0
  43. data/test/snapshots/icanhaz/match-error.txt +1 -0
  44. data/test/snapshots/icanhaz/simple_import.rb +5 -0
  45. data/test/snapshots/icanhaz/source.lt3 +10 -0
  46. data/test/snapshots/icanhaz2/expected-error.txt +0 -0
  47. data/test/snapshots/icanhaz2/expected-output.txt +6 -0
  48. data/test/snapshots/icanhaz2/simple_canhaz.rb +5 -0
  49. data/test/snapshots/icanhaz2/source.lt3 +6 -0
  50. data/test/snapshots/simple_import/expected-error.txt +0 -0
  51. data/test/snapshots/simple_import/expected-output.txt +7 -0
  52. data/test/snapshots/simple_import/simple_import.rb +5 -0
  53. data/test/snapshots/simple_import/source.lt3 +7 -0
  54. data/test/snapshots/simple_include/source.lt3 +0 -1
  55. data/test/snapshots.rb +85 -37
  56. data/test/unit/all.rb +1 -0
  57. data/test/unit/formatline.rb +650 -0
  58. data/test/unit/parser/importable.rb +1 -1
  59. data/test/unit/parser/mixin.rb +1 -1
  60. data/test/unit/parser/set.rb +19 -12
  61. data/test/unit/parser/string.rb +14 -14
  62. metadata +27 -5
  63. data/test/formatting-tests.rb +0 -35
  64. data/test/formatting.rb +0 -103
  65. data/test/snapshots/formatting-tests.txt +0 -124
@@ -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
@@ -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/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
- @line = line
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(line)
39
+ t = x.tokenize
40
+ # TTY.puts "tokens = \n#{t.inspect}\n "
49
41
  x.evaluate
50
42
  end
51
43
 
52
- def tokenize(line)
53
- grab
44
+ def tokenize
45
+ # add grab
54
46
  loop do
55
- case curr
56
- when Escape; grab; add curr; grab; add curr
47
+ case peek
48
+ when Escape; grab; add peek; grab; add peek
57
49
  when "$"
58
50
  dollar
59
51
  when "*", "_", "`", "~"
60
- marker curr
61
- add curr
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 curr
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.grab
79
- loop do
80
- case x.curr
81
- when Escape; x.grab; x.add x.curr; x.grab
82
- when "$"
83
- x.dollar
84
- when LF, nil
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 curr
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 curr.nil?
211
- str << curr
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 curr.nil?
223
- str << curr
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 curr
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 = curr + grab_alpha_dot
220
+ var = peek + grab_alpha_dot
241
221
  add_token(:var, var)
242
222
  else
243
- add "$" + curr
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, "$$" + curr; return
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 curr
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 = curr + collect!(sym, Blank)
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 curr == Escape
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, curr)
307
+ if terminate?(terminators, peek)
328
308
  break
329
309
  end
330
- # STDERR.puts "#{curr.inspect} is not a terminator"
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 curr == "]" # skip right bracket
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 curr.nil?
337
+ when peek.nil?
360
338
  return str
361
- when curr == Escape
339
+ when peek == Escape
362
340
  str << escaped
363
341
  next
364
- when terminate?(terminators, curr)
342
+ when terminate?(terminators, peek)
365
343
  break
366
344
  else
367
- str << curr # not a terminator
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