livetext 0.9.17 → 0.9.22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/README.lt3 +2 -0
  3. data/README.md +330 -339
  4. data/bin/livetext +0 -2
  5. data/imports/bookish.rb +286 -0
  6. data/imports/calibre.rb +28 -0
  7. data/imports/livemagick.rb +133 -0
  8. data/imports/markdown.rb +44 -0
  9. data/imports/markdown_importable.rb +45 -0
  10. data/imports/pyggish.rb +172 -0
  11. data/imports/tutorial.rb +95 -0
  12. data/lib/cmdargs.rb +28 -20
  13. data/lib/formatline.rb +103 -75
  14. data/lib/functions.rb +16 -1
  15. data/lib/handler/{icanhaz.rb → import.rb} +1 -1
  16. data/lib/handler.rb +1 -1
  17. data/lib/helpers.rb +34 -8
  18. data/lib/livetext.rb +12 -5
  19. data/lib/parser/general.rb +1 -1
  20. data/lib/parser/set.rb +3 -5
  21. data/lib/parser/string.rb +11 -10
  22. data/lib/processor.rb +25 -13
  23. data/lib/standard.rb +16 -16
  24. data/lib/userapi.rb +6 -4
  25. data/livetext.gemspec +2 -1
  26. data/plugin/bookish.rb +4 -5
  27. data/plugin/markdown.rb +6 -6
  28. data/plugin/pyggish.rb +45 -77
  29. data/plugin/tutorial.rb +3 -3
  30. data/test/snapshots/error_inc_line_num/actual-error.txt +14 -0
  31. data/test/snapshots/error_inc_line_num/actual-output.txt +7 -0
  32. data/test/snapshots/error_inc_line_num/out-sdiff.txt +14 -0
  33. data/test/snapshots/error_invalid_name/actual-error.txt +10 -0
  34. data/test/snapshots/{icanhaz2/expected-error.txt → error_invalid_name/actual-output.txt} +0 -0
  35. data/test/snapshots/error_invalid_name/out-sdiff.txt +6 -0
  36. data/test/snapshots/error_missing_end/actual-error.txt +10 -0
  37. data/test/snapshots/error_missing_end/actual-output.txt +0 -0
  38. data/test/snapshots/error_missing_end/out-sdiff.txt +6 -0
  39. data/test/snapshots/error_no_such_copy/actual-error.txt +10 -0
  40. data/test/snapshots/error_no_such_copy/actual-output.txt +0 -0
  41. data/test/snapshots/error_no_such_inc/actual-error.txt +10 -0
  42. data/test/snapshots/error_no_such_inc/actual-output.txt +0 -0
  43. data/test/snapshots/error_no_such_mixin/actual-error.txt +13 -0
  44. data/test/snapshots/error_no_such_mixin/actual-output.txt +0 -0
  45. data/test/snapshots/error_no_such_mixin/out-sdiff.txt +6 -0
  46. data/test/snapshots/import/actual-error.txt +13 -0
  47. data/test/snapshots/import/actual-output.txt +0 -0
  48. data/test/snapshots/{icanhaz → import}/expected-output.txt +0 -0
  49. data/test/snapshots/{icanhaz → import}/match-error.txt +0 -0
  50. data/test/snapshots/import/out-sdiff.txt +6 -0
  51. data/test/snapshots/{icanhaz → import}/simple_import.rb +0 -0
  52. data/test/snapshots/{icanhaz → import}/source.lt3 +2 -2
  53. data/test/snapshots/import2/expected-error.txt +0 -0
  54. data/test/snapshots/{icanhaz2 → import2}/expected-output.txt +3 -1
  55. data/test/snapshots/{icanhaz2/simple_canhaz.rb → import2/simple_import.rb} +0 -0
  56. data/test/snapshots/import2/source.lt3 +8 -0
  57. data/test/snapshots/more_functions/expected-error.txt +0 -0
  58. data/test/snapshots/more_functions/expected-output.txt +37 -0
  59. data/test/snapshots/more_functions/source.lt3 +40 -0
  60. data/test/snapshots/simple_import/expected-output.txt +2 -0
  61. data/test/snapshots/simple_import/source.lt3 +3 -1
  62. data/test/snapshots/subset.txt +83 -0
  63. data/test/snapshots/wtf_bookish/expected-error.txt +0 -0
  64. data/test/snapshots/wtf_bookish/expected-output.txt +10 -0
  65. data/test/snapshots/wtf_bookish/source.lt3 +7 -0
  66. data/test/snapshots/wtf_bookish/toc.tmp +0 -0
  67. data/test/snapshots.rb +71 -46
  68. data/test/unit/formatline.rb +252 -135
  69. data/test/unit/parser/set.rb +20 -22
  70. data/test/unit/parser/string.rb +45 -6
  71. data/test/unit/standard.rb +0 -1
  72. metadata +44 -22
  73. data/lib/livetext/importable.rb +0 -2
  74. data/lib/parser/import.rb +0 -15
  75. data/test/affirm/kbks.jpg +0 -0
  76. data/test/affirm/lm-kbks.lt +0 -19
  77. data/test/cleanup +0 -1
  78. data/test/newtest +0 -14
  79. data/test/sdtest +0 -6
  80. data/test/snapshots/OMIT.txt +0 -11
  81. data/test/snapshots/clusion.txt +0 -70
  82. data/test/snapshots/crap +0 -16
  83. data/test/snapshots/fixit +0 -6
  84. data/test/snapshots/icanhaz2/source.lt3 +0 -6
@@ -0,0 +1,172 @@
1
+ require 'rouge'
2
+
3
+ # FIXME This file has suboptimal juju. See plugin/pyggish.rb also
4
+
5
+ module Pyggish
6
+ def self.pyg_change(code, klass, style)
7
+ color = style[0..6]
8
+ modifier = style[8]
9
+ mod_open = modifier ? "<#{modifier}>" : ""
10
+ mod_close = modifier ? "</#{modifier}>" : ""
11
+ rx = /<span class="#{klass}">(?<cname>[^<]+?)<\/span>/
12
+ loop do
13
+ md = rx.match(code)
14
+ break if md.nil?
15
+ str = md[:cname]
16
+ result = code.sub!(rx, "<font color=#{color}>#{mod_open}#{str}#{mod_close}</font>")
17
+ break if result.nil?
18
+ end
19
+ end
20
+
21
+ def self._codebar_color(lexer)
22
+ color = case lexer
23
+ when :elixir
24
+ "#fc88fc"
25
+ when :ruby
26
+ "#fc8888"
27
+ else
28
+ raise "Unknown lexer"
29
+ end
30
+ end
31
+
32
+ def self.pyg_finalize(code, lexer=:elixir)
33
+ Styles.each_pair {|klass, style| pyg_change(code, klass, style) }
34
+ code.sub!(/<pre>/, "<pre>\n")
35
+ code.gsub!(/<span class="[np]">/, "")
36
+ code.gsub!(/<\/span>/, "")
37
+ color = _codebar_color(lexer)
38
+ code.sub!(/<td class="linenos"/, "<td width=2%></td><td width=5% bgcolor=#{color}")
39
+ code.gsub!(/<td/, "<td valign=top ")
40
+ code.gsub!(/ class="[^"]*?"/, "") # Get rid of remaining Pygments CSS
41
+ lines = code.split("\n")
42
+ n1 = lines.index {|x| x =~ /<pre>/ }
43
+ n2 = lines.index {|x| x =~ /<\/pre>/ }
44
+ # FIXME ?
45
+ n1 ||= 0
46
+ n2 ||= -1
47
+ lines[n1].sub!(/ 1$/, " 1 ")
48
+ (n1+1).upto(n2) {|n| lines[n].replace(" " + lines[n] + " ") }
49
+ code = lines.join("\n")
50
+ code
51
+ end
52
+
53
+ def _process_code(text)
54
+ lines = text.split("\n")
55
+ lines = lines.select {|x| x !~ /##~ omit/ }
56
+ @refs = {}
57
+ lines.each.with_index do |line, i|
58
+ if line =~ /##~ ref/
59
+ frag, name = line.split(/ *##~ ref/)
60
+ @refs[name.strip] = i
61
+ line.replace(frag)
62
+ end
63
+ end
64
+ lines.map! {|line| " " + line }
65
+ text2 = lines.join("\n")
66
+ text.replace(text2)
67
+ end
68
+
69
+ def _colorize(code, lexer=:elixir)
70
+ text = ::Pygments.highlight(code, lexer: lexer, options: {linenos: "table"})
71
+ _debug "--- in _colorize: text = #{text.inspect}"
72
+ text2 = PygmentFix.pyg_finalize(text, lexer)
73
+ result = "<!-- colorized code -->\n" + text2
74
+ result
75
+ end
76
+
77
+ def _colorize!(code, lexer=:elixir)
78
+ text = ::Pygments.highlight(code, lexer: lexer, options: {})
79
+ _debug "--- in _colorize!: text = #{text.inspect}"
80
+ text2 = PygmentFix.pyg_finalize(text, lexer)
81
+ result = "<!-- colorized code -->\n" + text2
82
+ result
83
+ end
84
+
85
+ def fragment
86
+ lang = @_args.empty? ? :elixir : @_args.first.to_sym # ruby or elixir
87
+ @_args = []
88
+ send(lang)
89
+ _out "\n"
90
+ end
91
+
92
+ def code # FIXME ?
93
+ text = ""
94
+ _body {|line| _out " " + line }
95
+ end
96
+
97
+ def mono
98
+ _out "<pre>"
99
+ _body {|line| _out " " + line }
100
+ _out "</pre>"
101
+ end
102
+
103
+ def create_code_styles
104
+ dir = @_outdir || "."
105
+ theme, back = "Github", "white"
106
+ css = Rouge::Themes.const_get(theme).render(scope: '.rb_highlight')
107
+ added = <<~CSS
108
+ .rb_highlight {
109
+ font-family: 'Monaco', 'Andale Mono', 'Lucida Grande', 'Courier', 'Lucida Console', 'Courier New', monospace;
110
+ white-space: pre;
111
+ background-color: #{back}
112
+ }
113
+ CSS
114
+
115
+ css.gsub!(/{\n/, "{\n font-family: courier;")
116
+ css = added + "\n" + css
117
+ # STDERR.puts "Writing #{theme} theme to ruby.css"
118
+ File.write("#{dir}/ruby.css", css)
119
+
120
+ css = Rouge::Themes.const_get(theme).render(scope: '.ex_highlight')
121
+ added = added.sub(/rb/, "ex")
122
+ css.gsub!(/{\n/, "{\n font-family: courier;")
123
+ css = added + "\n" + css
124
+ # STDERR.puts "Writing #{theme} theme to elixir.css"
125
+ File.write("#{dir}/elixir.css", css)
126
+ end
127
+
128
+
129
+ def format_ruby(source, theme = "Github", back = "black")
130
+ # theme/back not used now
131
+ formatter = Rouge::Formatters::HTML.new
132
+ lexer = Rouge::Lexers::Ruby.new
133
+ body = formatter.format(lexer.lex(source))
134
+ text = "<div class=rb_highlight>#{body}</div>"
135
+ text
136
+ end
137
+
138
+ def format_elixir(source, theme = "Github", back = "black")
139
+ # theme/back not used now
140
+ formatter = Rouge::Formatters::HTML.new
141
+ lexer = Rouge::Lexers::Elixir.new
142
+ body = formatter.format(lexer.lex(source))
143
+ text = "<div class=ex_highlight>#{body}</div>"
144
+ text
145
+ end
146
+
147
+ def ruby
148
+ file = @_args.first
149
+ if file.nil?
150
+ code = " # Ruby code\n\n"
151
+ _body {|line| code << " " + line + "\n" }
152
+ else
153
+ code = "# Ruby code\n\n" + ::File.read(file)
154
+ end
155
+
156
+ html = format_ruby(code)
157
+ _out html
158
+ end
159
+
160
+ def elixir
161
+ file = @_args.first
162
+ if file.nil?
163
+ code = ""
164
+ _body {|line| code << " " + line + "\n" }
165
+ else
166
+ code = ::File.read(file)
167
+ end
168
+
169
+ html = format_elixir(code)
170
+ _out html
171
+ end
172
+ end
@@ -0,0 +1,95 @@
1
+ require 'helpers'
2
+
3
+ # Created as an "ad hoc" lib for generating the README
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
- .foobar
7
+ .foobar
8
+
8
9
  2. A dot-command (left-indented) usually looks like:
9
- $.foobar
10
+ $.foobar
11
+
10
12
  3. More generally, it may have any number of parameters (0, 1, ...)
11
- .redirect somefile.txt append
13
+ .redirect somefile.txt append
14
+
12
15
  4. Variables and functions *can* appear (rare in practice??)
13
- .redirect somefile$my_suffix $$my_mode
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
- 7. Some commands have NO body while others have an OPTIONAL or REQUIRED body
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 PERMITS a body
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
 
@@ -56,19 +65,18 @@ class Livetext::CmdData
56
65
  # body: true => this command has a body + .end
57
66
  # how raw is raw?
58
67
  # remove comment - always/sometimes/never?
59
- # var_func_parse - always/sometimes/never?
60
- # var_func_parse inside body??
68
+ # interpolate - always/sometimes/never?
69
+ # interpolate inside body??
61
70
  @data = data.dup # comment? vars? funcs?
62
71
  @raw = data.dup # comment? vars? funcs?
63
72
  @args = data.split # simple array
64
73
  @nargs = nargs # not really "needed"
65
74
  check_num_args(nargs)
66
-
67
- # @varfunc = _var_func_parse(data.dup)
75
+ # @varfunc = Livetext.interpolate(data.dup)
68
76
  end
69
77
 
70
78
  def check_num_args(num)
71
- num_range = /(\d{0,2})(\.\.)(\d{0,2})/
79
+ num_range = /(\d{0,2})(\.\.)(\d{0,2})/ # Not "really" right...
72
80
  min, max = 0, 9999
73
81
  md = num_range.match(@nargs).to_a
74
82
  bad_args = nil
data/lib/formatline.rb CHANGED
@@ -7,6 +7,8 @@ class FormatLine < StringParser
7
7
  SimpleFormats[:i] = %w[<i> </i>]
8
8
  SimpleFormats[:t] = ["<font size=+1><tt>", "</tt></font>"]
9
9
  SimpleFormats[:s] = %w[<strike> </strike>]
10
+
11
+ BITS = SimpleFormats.keys
10
12
 
11
13
  Null = ""
12
14
  Space = " "
@@ -45,14 +47,14 @@ class FormatLine < StringParser
45
47
  # add grab
46
48
  loop do
47
49
  case peek
48
- when Escape; grab; add peek; grab; add peek
50
+ when Escape; grab; add peek; grab; # add peek # <-- last is an error??
49
51
  when "$"
50
52
  dollar
51
53
  when "*", "_", "`", "~"
52
54
  marker peek
53
55
  add peek
54
56
  when LF
55
- break if @i >= line.size - 1
57
+ break if eos? # @i >= line.size - 1
56
58
  when nil
57
59
  break
58
60
  else
@@ -107,22 +109,10 @@ class FormatLine < StringParser
107
109
  break if token.nil?
108
110
  sym, val = *token
109
111
  case sym
110
- when :str
111
- @out << val unless val == "\n" # BUG
112
- when :var
113
- @out << varsub(val)
114
- when :func
115
- param = nil
116
- arg = gen.peek
117
- if [:colon, :brackets].include? arg[0]
118
- arg = gen.next # for real
119
- param = arg[1]
120
- param = FormatLine.var_func_parse(param)
121
- end
122
- @out << funcall(val, param)
123
- when :b, :i, :t, :s
124
- val = FormatLine.var_func_parse(val)
125
- @out << embed(sym, val)
112
+ when :str; eval_str(val)
113
+ when :var; eval_var(val)
114
+ when :func; eval_func(val, gen)
115
+ when *BITS; eval_bits(sym, val)
126
116
  else
127
117
  add_token :str
128
118
  end
@@ -131,46 +121,16 @@ class FormatLine < StringParser
131
121
  @out
132
122
  end
133
123
 
134
- def grab_colon_param
135
- grab # grab :
136
- param = ""
137
- loop do
138
- case next!
139
- when Escape
140
- grab
141
- param << next!
142
- grab
143
- when Space, LF, nil; break
144
- else
145
- param << next!
146
- grab
147
- end
148
- end
149
-
150
- param = nil if param.empty?
151
- param
152
- end
153
-
154
124
  def grab_func_param
155
- grab # [
156
- param = ""
157
- loop do
158
- case next!
159
- when Escape
160
- grab
161
- param << next!
162
- grab
163
- when "]", LF, nil; break
164
- else
165
- param << next!
166
- grab
167
- end
125
+ case lookahead
126
+ when "["
127
+ param = grab_bracket_param
128
+ add_token(:brackets, param)
129
+ when ":"
130
+ param = grab_colon_param
131
+ add_token(:colon, param)
132
+ else # do nothing
168
133
  end
169
-
170
- add peek
171
- grab
172
- param = nil if param.empty?
173
- param
174
134
  end
175
135
 
176
136
  def add(str)
@@ -189,7 +149,7 @@ class FormatLine < StringParser
189
149
  loop do
190
150
  break if eos?
191
151
  str << peek
192
- break if terminate?(NoAlpha, next!)
152
+ break if terminate?(NoAlpha, lookahead)
193
153
  grab
194
154
  end
195
155
  str
@@ -199,9 +159,9 @@ class FormatLine < StringParser
199
159
  str = Null.dup
200
160
  grab
201
161
  loop do
202
- break if eos? # peek.nil?
162
+ break if peek.nil? # eos?
203
163
  str << peek
204
- break if terminate?(NoAlphaDot, next!)
164
+ break if terminate?(NoAlphaDot, lookahead)
205
165
  grab
206
166
  end
207
167
  str
@@ -216,7 +176,7 @@ class FormatLine < StringParser
216
176
  when "$"; double_dollar
217
177
  # when "."; dollar_dot
218
178
  when /[A-Za-z]/
219
- add_token :str
179
+ add_token :str
220
180
  var = peek + grab_alpha_dot
221
181
  add_token(:var, var)
222
182
  else
@@ -226,17 +186,14 @@ class FormatLine < StringParser
226
186
  end
227
187
 
228
188
  def double_dollar
229
- case next!
189
+ case lookahead
230
190
  when Space; add_token :string, "$$ "; grab; return
231
191
  when LF, nil; add "$$"; add_token :str
232
192
  when Alpha
233
193
  add_token(:str, @token)
234
194
  func = grab_alpha
235
195
  add_token(:func, func)
236
- case next!
237
- when ":"; param = grab_colon_param; add_token(:colon, param)
238
- when "["; param = grab_func_param; add_token(:brackets, param)
239
- end
196
+ param = grab_func_param # may be null/missing
240
197
  else
241
198
  grab; add_token :str, "$$" + peek; return
242
199
  end
@@ -276,7 +233,7 @@ class FormatLine < StringParser
276
233
  def double_marker(char)
277
234
  sym = Syms[char]
278
235
  kind = sym
279
- case next! # first char after **
236
+ case lookahead # first char after **
280
237
  when Space, LF, nil
281
238
  pre, post = SimpleFormats[sym]
282
239
  add_token kind
@@ -317,13 +274,12 @@ class FormatLine < StringParser
317
274
  add str
318
275
  str
319
276
  rescue => err
320
- STDERR.puts "ERR = #{err}\n#{err.backtrace}"
321
- STDERR.puts "=== str = #{str.inspect}"
277
+ ::STDERR.puts "ERR = #{err}\n#{err.backtrace}"
322
278
  end
323
279
 
324
280
  def escaped
325
- grab
326
- ch = grab
281
+ grab # Eat the backslash
282
+ ch = grab # Take next char
327
283
  ch
328
284
  end
329
285
 
@@ -350,20 +306,20 @@ class FormatLine < StringParser
350
306
  add str
351
307
  str
352
308
  rescue => err
353
- STDERR.puts "ERR = #{err}\n#{err.backtrace}"
354
- STDERR.puts "=== str = #{str.inspect}"
309
+ ::STDERR.puts "ERR = #{err}\n#{err.backtrace}"
355
310
  end
356
311
 
357
312
  def funcall(name, param)
358
313
  err = "[Error evaluating $$#{name}(#{param})]"
314
+ func_name = name # "func_" + name.to_s
359
315
  result =
360
- if self.respond_to?("func_" + name.to_s)
361
- self.send("func_" + name.to_s, param)
316
+ if self.send?(func_name, param) # self.respond_to?(func_name)
317
+ # do nothing
362
318
  else
363
319
  fobj = ::Livetext::Functions.new
364
320
  fobj.send(name, param) rescue err
365
321
  end
366
- result
322
+ result.to_s
367
323
  end
368
324
 
369
325
  def varsub(name)
@@ -374,4 +330,76 @@ class FormatLine < StringParser
374
330
  def embedded?
375
331
  ! (['"', "'", " ", nil].include? prev)
376
332
  end
333
+
334
+ private
335
+
336
+ def grab_colon_param
337
+ grab # grab :
338
+ param = ""
339
+ loop do
340
+ case lookahead
341
+ when Escape
342
+ grab
343
+ param << lookahead
344
+ grab
345
+ when Space, LF, nil; break
346
+ else
347
+ param << lookahead
348
+ grab
349
+ end
350
+ end
351
+
352
+ param = nil if param.empty?
353
+ param
354
+ end
355
+
356
+ def grab_bracket_param
357
+ grab # [
358
+ param = ""
359
+ loop do
360
+ case lookahead
361
+ when Escape
362
+ grab
363
+ param << lookahead
364
+ grab
365
+ when "]", LF, nil
366
+ break
367
+ else
368
+ param << lookahead
369
+ grab
370
+ end
371
+ end
372
+ add peek
373
+ grab
374
+ param = nil if param.empty?
375
+ param
376
+ end
377
+
378
+ def eval_bits(sym, val)
379
+ val = Livetext.interpolate(val)
380
+ @out << embed(sym, val)
381
+ end
382
+
383
+ def eval_func(val, gen)
384
+ param = nil
385
+ arg = gen.peek rescue :bogus
386
+ unless arg == :bogus
387
+ if [:colon, :brackets].include? arg[0]
388
+ arg = gen.next # for real
389
+ param = arg[1]
390
+ # FIXME - unsure - interpolate again??
391
+ # param = Livetext.interpolate(param)
392
+ end
393
+ end
394
+ @out << funcall(val, param)
395
+ end
396
+
397
+ def eval_var(val)
398
+ @out << varsub(val)
399
+ end
400
+
401
+ def eval_str(val)
402
+ @out << val unless val == "\n" # BUG
403
+ end
404
+
377
405
  end
data/lib/functions.rb CHANGED
@@ -13,6 +13,21 @@ class Livetext::Functions
13
13
  attr_accessor :param # kill this?
14
14
  end
15
15
 
16
+ # FIXME Function parameters need to be fixed...
17
+
18
+ def isqrt(param = nil) # "integer square root" - Just for testing
19
+ arg = num = param # Takes any number
20
+ if num.nil? || num.empty?
21
+ arg = "NO PARAM" # Just for error text
22
+ end
23
+ # Integer()/Float() can raise error
24
+ num = num.include?(".") ? Float(num) : Integer(num)
25
+ # Returns truncated integer
26
+ Math.sqrt(num).to_i # user need not do to_s
27
+ rescue => err # Malformed number? negative?
28
+ "[Error evaluating $$isqrt(#{arg})]"
29
+ end
30
+
16
31
  def date(param=nil)
17
32
  Time.now.strftime("%F")
18
33
  end
@@ -40,7 +55,7 @@ class Livetext::Functions
40
55
  "<br>"*n
41
56
  end
42
57
 
43
- def yt(param)
58
+ def yt(param) # FIXME uh, this is crap
44
59
  param = self.class.param
45
60
  "https://www.youtube.com/watch?v=#{param}"
46
61
  end
@@ -1,5 +1,5 @@
1
1
 
2
- class Livetext::Handler::ICanHaz
2
+ class Livetext::Handler::Import
3
3
  include Helpers
4
4
 
5
5
  attr_reader :file
data/lib/handler.rb CHANGED
@@ -1 +1 @@
1
- require_relative 'handler/icanhaz'
1
+ require_relative 'handler/import'