livetext 0.9.25 → 0.9.30

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/README.lt3 +3 -2
  3. data/imports/bookish.rb +1 -2
  4. data/lib/livetext/errors.rb +3 -0
  5. data/lib/livetext/expansion.rb +106 -0
  6. data/lib/livetext/formatter.rb +104 -0
  7. data/lib/livetext/functions.rb +9 -0
  8. data/lib/livetext/global_helpers.rb +5 -0
  9. data/lib/livetext/handler/import.rb +2 -6
  10. data/lib/livetext/handler/mixin.rb +2 -6
  11. data/lib/livetext/helpers.rb +30 -27
  12. data/lib/livetext/html.rb +73 -0
  13. data/lib/livetext/more.rb +178 -0
  14. data/lib/livetext/parser/general.rb +1 -1
  15. data/lib/livetext/parser/set.rb +10 -3
  16. data/lib/livetext/parser/string.rb +14 -5
  17. data/lib/livetext/processor.rb +8 -1
  18. data/lib/livetext/skeleton.rb +2 -1
  19. data/lib/livetext/standard.rb +30 -16
  20. data/lib/livetext/userapi.rb +61 -22
  21. data/lib/livetext/version.rb +1 -1
  22. data/lib/livetext.rb +2 -152
  23. data/plugin/bootstrap_menu.rb +140 -0
  24. data/plugin/misc/navbar.rb +162 -0
  25. data/test/snapshots/basic_formatting/expected-output.txt +1 -2
  26. data/test/snapshots/{import_bookish/toc.tmp → bootstrap_menu/expected-error.txt} +0 -0
  27. data/test/snapshots/bootstrap_menu/expected-output.txt +4 -0
  28. data/test/snapshots/bootstrap_menu/source.lt3 +17 -0
  29. data/test/snapshots/error_invalid_name/foo +5 -0
  30. data/test/snapshots/import_bookish/expected-output.txt +4 -4
  31. data/test/snapshots/more_functions/expected-output.txt +1 -1
  32. data/test/snapshots/more_functions/source.lt3 +1 -1
  33. data/test/snapshots/subset.txt +50 -46
  34. data/test/snapshots/{mixin_bookish/toc.tmp → var_into_func/expected-error.txt} +0 -0
  35. data/test/snapshots/var_into_func/expected-output.txt +16 -0
  36. data/test/snapshots/var_into_func/source.lt3 +16 -0
  37. data/test/unit/all.rb +2 -1
  38. data/test/unit/lineparser.rb +359 -0
  39. data/test/unit/new_lineparser.rb +359 -0
  40. data/test/unit/parser/general.rb +2 -2
  41. data/test/unit/parser/set.rb +12 -20
  42. metadata +16 -11
  43. data/lib/livetext/formatline.rb +0 -321
  44. data/lib/livetext/funcall.rb +0 -84
  45. data/test/snapshots/error_inc_line_num/OUT +0 -17
  46. data/test/snapshots/error_no_such_copy/duh +0 -26
  47. data/test/snapshots/error_no_such_copy/mystery.txt +0 -36
  48. data/test/testlines.rb +0 -37
  49. data/test/unit/formatline.rb +0 -769
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f447e149e6724975184b8db2c51ff59c58752c3cb5b4037bb86fb1f355015a6
4
- data.tar.gz: 9740fc3b2bb98e92c91c523656f270a66132d882fbcaa9b1a803294d1e10a65a
3
+ metadata.gz: ee3796664666616d6b5428f4b27f81c3f902171fc8afc8fd351631c6c774e730
4
+ data.tar.gz: 5dd5250915ef13d930dca6b147e84b5e04e42d1a7b9c0f4b1e95d510ea49e476
5
5
  SHA512:
6
- metadata.gz: 1ee34071fde07e45b13d8f13aa9889d2b060651901acd8e3876071121ea5222c8bc4b83162c30f772c53fe92e07ea7bada3f28be0c75994f1ad21bcdacbb2905
7
- data.tar.gz: ce9ed1ac2cd624818b775d19681a406f0a4c43c2a0b9a7f6f8c1f8fb9834b91301a2b65c10d1faa0c4b664a1abcb741c570d1745d996537f4d438e97b65e3f56
6
+ metadata.gz: d0e81ca1d3990468ae49f3c5e23ab608160f7db301f692757200fe57b5434f125bd83b3a7af80dd22ea8cc278972dcb39fe3b408f3a800871f3fb243e6c3639c
7
+ data.tar.gz: 4bc7641f62f63215fbcf52192a74956991e540a2282922da21a5b0ea2a5d959420a9226c52745c635fc91521d860921b4d7d69defb8b540868d73df9b8fbfd99
data/README.lt3 CHANGED
@@ -7,8 +7,9 @@
7
7
 
8
8
  *[This README is currently mangled. Fixes coming soon!]
9
9
 
10
- Livetext is simply a tool for transforming text from one format into another. The source file
11
- has commands embedded in it, and the output is dependent on those commands.
10
+ Livetext is simply a tool for transforming text another format which may be HTML or some arbitrary
11
+ format (text or otherwise). The source file has commands embedded in it, and the output is dependent
12
+ on those commands.
12
13
 
13
14
  Why is this special? It's very flexible, very extensible, and it's extensible _[in Ruby].
14
15
 
data/imports/bookish.rb CHANGED
@@ -187,8 +187,7 @@ module Bookish
187
187
  cells = line.split(delim)
188
188
  api.out "<tr>"
189
189
  cells.each.with_index do |cell, i|
190
- api.out " <td width=#{maxw}% valign=top>" +
191
- "#{cell}</td>"
190
+ api.out " <td width=#{maxw[i]}% valign=top>#{cell}</td>"
192
191
  end
193
192
  api.out "</tr>"
194
193
  end
@@ -13,3 +13,6 @@ def make_exception(sym, str, target_class = Object)
13
13
  end
14
14
  end
15
15
 
16
+ make_exception(:EndWithoutOpening, "Error: found .end with no opening command")
17
+ make_exception(:UnknownMethod, "Error: name '%1' is unknown")
18
+
@@ -0,0 +1,106 @@
1
+
2
+ class Livetext::Expansion
3
+
4
+ Ident = "[[:alpha:]]([[:alnum:]]|_)*"
5
+ Dotted = "#{Ident}(\\.#{Ident})*"
6
+ Func = "\\$\\$"
7
+ Var = "\\$"
8
+ Lbrack = "\\["
9
+ Colon = ":"
10
+
11
+ def initialize(instance)
12
+ @live = instance
13
+ end
14
+
15
+ def format(line) # new/experimental
16
+ return "" if line == "\n" || line.nil?
17
+ formatted = Formatter.format(line)
18
+ with_vars = expand_variables(formatted)
19
+ with_func = expand_function_calls(with_vars)
20
+ end
21
+
22
+ def expand_variables(str)
23
+ rx = Regexp.compile("(?<result>" + Var + Dotted + ")")
24
+ enum = str.each_char
25
+ buffer = ""
26
+ loop do |i|
27
+ case # var or func or false alarm
28
+ when str.empty? # end of string
29
+ break
30
+ when str.slice(0..1) == "$$" # func?
31
+ buffer << str.slice!(0..1)
32
+ when str.slice(0) == "$" # var?
33
+ vmatch = rx.match(str)
34
+ if vmatch.nil?
35
+ buffer << str.slice!(0)
36
+ next
37
+ end
38
+ vname = vmatch["result"]
39
+ str.sub!(vname, "")
40
+ vsym = vname[1..-1].to_sym
41
+ vars = @live.vars
42
+ buffer << vars.get(vsym)
43
+ else # other
44
+ buffer << str.slice!(0)
45
+ end
46
+ end
47
+ buffer
48
+ end
49
+
50
+ def funcall(name, param)
51
+ err = "[Error evaluating $$#{name}(#{param})]"
52
+ name = name.gsub(/\./, "__")
53
+ return if self.send?(name, param)
54
+ fobj = ::Livetext::Functions.new
55
+ result = fobj.send(name, param) rescue err
56
+ result.to_s
57
+ end
58
+
59
+ def expand_function_calls(str)
60
+ # Assume variables already resolved
61
+ pat1 = "(?<result>" + Func + Dotted + ")"
62
+ colon = ":"
63
+ lbrack = "\\["
64
+ rbrack = "\\]"
65
+ space_eol = "( |$)"
66
+ prx1 = "(?<param>[^ ]+)"
67
+ prx2 = "(?<param>.+)"
68
+ pat2 = "(?<full_param>#{colon}#{prx1})"
69
+ pat3 = "(?<full_param>#{lbrack}#{prx2}#{rbrack})"
70
+ rx = Regexp.compile("#{pat1}(#{pat2}|#{pat3})?")
71
+
72
+ buffer = ""
73
+ loop do |i|
74
+ case # Var or Func or false alarm
75
+ when str.nil?
76
+ return buffer
77
+ when str.empty? # end of string
78
+ break
79
+ when str.slice(0..1) == "$$" # Func?
80
+ fmatch = rx.match(str)
81
+ fname = fmatch["result"] # includes $$
82
+ param = fmatch["param"] # may be nil
83
+ full = fmatch["full_param"]
84
+ fsym = fname[2..-1] # no $$
85
+ =begin
86
+ puts "rx = #{rx.inspect}"
87
+ puts "fmatch = #{fmatch.inspect}"
88
+ puts "fname = #{fname.inspect}"
89
+ puts "param = #{param.inspect}"
90
+ puts "full = #{full.inspect}"
91
+ puts "fsym = #{fsym.inspect}"
92
+ =end
93
+ str.sub!(fname, "")
94
+ str.sub!(full, "") if full
95
+ retval = funcall(fsym, param)
96
+ # puts "retval = #{retval.inspect}"
97
+ buffer << retval
98
+ else # other
99
+ char = str.slice!(0)
100
+ buffer << char
101
+ end
102
+ end
103
+ buffer
104
+ end
105
+ end
106
+
@@ -0,0 +1,104 @@
1
+ module Formatter
2
+
3
+ Start = /(?<start>(^| ))/
4
+ Stop = /(?<stop> |$)/
5
+
6
+ def self.make_regex(sigil, cdata, stop = Stop)
7
+ rx = /#{Start}#{sigil}#{cdata}#{stop}/
8
+ end
9
+
10
+ def self.iterate(str, rx, tag)
11
+ loop do
12
+ str, more = make_string(str, rx, tag)
13
+ break unless more
14
+ end
15
+ str
16
+ end
17
+
18
+ def self.double(str, char, tag)
19
+ cdata = /(?<cdata>[^ \.,]*?)/
20
+ stop = /(?<stop>[\.,]|$)/
21
+ sigil = Regexp.escape(char+char)
22
+ rx = make_regex(sigil, cdata, stop)
23
+ str = iterate(str, rx, tag)
24
+ str
25
+ end
26
+
27
+ def self.single(str, char, tag)
28
+ cdata = /((?<cdata>[^$ \[\*][^ ]*))/
29
+ sigil = Regexp.escape(char)
30
+ rx = make_regex(sigil, cdata)
31
+ str = iterate(str, rx, tag)
32
+ str
33
+ end
34
+
35
+ def self.bracket(str, char, tag)
36
+ cdata = /(?<cdata>[^\]]*)\]/
37
+ stop = /(?<stop> |$)/
38
+ sigil = Regexp.escape(char + "[")
39
+ rx = make_regex(sigil, cdata, stop)
40
+ str = iterate(str, rx, tag)
41
+ str
42
+ end
43
+
44
+ def self.make_string(str, rx, tag)
45
+ md = rx.match(str)
46
+ return [str, false] if md.nil?
47
+ start, cdata, stop = md.values_at(:start, :cdata, :stop)
48
+ str = str.sub(rx, start + "<#{tag}>" + cdata + "<\/#{tag}>" + stop)
49
+ [str, true]
50
+ end
51
+
52
+ def self.handle(str, char, tag)
53
+ s2 = double(str, char, tag) # in this order...
54
+ s2 = single(s2, char, tag)
55
+ s2 = bracket(s2, char, tag)
56
+ s2
57
+ end
58
+
59
+ def self.format(str)
60
+ s2 = str.chomp
61
+ s2 = handle(s2, "*", "b")
62
+ s2 = handle(s2, "_", "i")
63
+ s2 = handle(s2, "`", "tt")
64
+ s2 = handle(s2, "~", "strike")
65
+ s2
66
+ end
67
+ end
68
+
69
+ if $0 == __FILE__
70
+ # str = "**bold up front... This is **bold, __italic, and ``code."
71
+ str = "*bold and *bold and **more, and even *[more boldface] and *[still more] "
72
+ max = str.length
73
+ # str = "*bold "
74
+ s2 = Formatter.format(str)
75
+ printf "%-#{max}s => %-#{max}s\n", str.inspect, s2.inspect
76
+ exit
77
+
78
+ strings = ["*bold",
79
+ " *bold",
80
+ " *bold ",
81
+ "**bold.",
82
+ "**bold,",
83
+ "**bold",
84
+ " **bold.",
85
+ " **bold,",
86
+ " **bold",
87
+ " **bold. ",
88
+ " **bold, ",
89
+ " **bold ",
90
+ "*[fiat lux]",
91
+ " *[fiat lux]",
92
+ " *[fiat lux] ",
93
+ " *[fiat lux"
94
+ ]
95
+
96
+ max = strings.max_by {|s| s.length }
97
+ max = max.length + 2
98
+
99
+ strings.each do |str|
100
+ s2 = Formatter.format(str)
101
+ printf "%-#{max}s => %-#{max}s\n", str.inspect, s2.inspect
102
+ end
103
+
104
+ end
@@ -26,6 +26,15 @@ class Livetext::Functions
26
26
  "[Error evaluating $$isqrt(#{arg})]"
27
27
  end
28
28
 
29
+ def reverse(param=nil) # again, just for testing
30
+ arg = param
31
+ if arg.nil? || arg.empty?
32
+ return "(reverse: No parameter)"
33
+ else
34
+ return arg.reverse
35
+ end
36
+ end
37
+
29
38
  def date(param=nil)
30
39
  Time.now.strftime("%F")
31
40
  end
@@ -2,6 +2,7 @@
2
2
  module GlobalHelpers
3
3
 
4
4
  def check_disallowed(name)
5
+ api.tty "GLOBAL cdis"
5
6
  raise DisallowedName(name) if disallowed?(name)
6
7
  end
7
8
 
@@ -34,4 +35,8 @@ module GlobalHelpers
34
35
  return nil
35
36
  end
36
37
 
38
+ def cwd_root?
39
+ File.dirname(File.expand_path(".")) == "/"
40
+ end
41
+
37
42
  end
@@ -1,6 +1,8 @@
1
1
 
2
2
  require_relative '../helpers'
3
3
 
4
+ # Handle a .import
5
+
4
6
  class Livetext::Handler::Import
5
7
  include Livetext::Helpers
6
8
  include GlobalHelpers
@@ -34,11 +36,5 @@ class Livetext::Handler::Import
34
36
  newmod # return actual module
35
37
  end
36
38
 
37
- private
38
-
39
- def cwd_root?
40
- File.dirname(File.expand_path(".")) == "/"
41
- end
42
-
43
39
  end
44
40
 
@@ -1,6 +1,8 @@
1
1
 
2
2
  require_relative '../helpers'
3
3
 
4
+ # Handle a .mixin
5
+
4
6
  class Livetext::Handler::Mixin
5
7
  include Livetext::Helpers
6
8
  include GlobalHelpers
@@ -27,11 +29,5 @@ class Livetext::Handler::Mixin
27
29
  [modname, "module ::#{modname}; #{meths}\nend"]
28
30
  end
29
31
 
30
- private
31
-
32
- def cwd_root?
33
- File.dirname(File.expand_path(".")) == "/"
34
- end
35
-
36
32
  end
37
33
 
@@ -1,5 +1,6 @@
1
1
 
2
2
  require_relative 'global_helpers'
3
+ require_relative 'expansion'
3
4
 
4
5
  module Livetext::Helpers
5
6
 
@@ -55,8 +56,10 @@ module Livetext::Helpers
55
56
  base = "#{name}#{ext}"
56
57
  paths.each do |path|
57
58
  file = path + base
59
+ # ::Livetext::TTY.puts " Checking: #{file}"
58
60
  return file if File.exist?(file)
59
61
  end
62
+ # ::Livetext::TTY.puts " ...oops"
60
63
  return nil
61
64
  end
62
65
 
@@ -92,12 +95,12 @@ module Livetext::Helpers
92
95
  def process_line(line)
93
96
  success = true
94
97
  case line # must apply these in order
95
- when Comment
96
- success = handle_scomment(line)
97
- when DotCmd
98
- success = handle_dotcmd(line)
99
- when DollarDot
100
- success = handle_dollar_dot
98
+ when Comment
99
+ success = handle_scomment(line)
100
+ when DotCmd
101
+ success = handle_dotcmd(line)
102
+ when DollarDot
103
+ success = handle_dollar_dot
101
104
  else
102
105
  api.passthru(line) # must succeed?
103
106
  end
@@ -113,18 +116,16 @@ module Livetext::Helpers
113
116
  success
114
117
  end
115
118
 
116
- def invoke_dotcmd(name)
119
+ def invoke_dotcmd(name, *args)
117
120
  # FIXME Add cmdargs stuff... depends on name, etc.
118
- retval = @main.send(name)
121
+ retval = @main.send(name, *args)
119
122
  retval
120
- # rescue NoMethodError => err
121
- # graceful_error(err)
122
123
  rescue => err
123
124
  graceful_error(err)
124
125
  end
125
126
 
126
127
  def handle_dotcmd(line, indent = 0)
127
- # TTY.puts ">>> #{__method__} in #{__FILE__}" # if ENV['debug']
128
+ # FIXME api.data is broken
128
129
  indent = @indentation.last # top of stack
129
130
  line = line.sub(/# .*$/, "") # FIXME Could be problematic?
130
131
  name = get_name(line)
@@ -159,15 +160,16 @@ module Livetext::Helpers
159
160
  end
160
161
 
161
162
  def check_file_exists(file)
162
- # raise FileNotFound(file) unless File.exist?(file)
163
- return File.exist?(file)
163
+ return File.exist?(file)
164
+ end
165
+
166
+ def read_variables(file)
167
+ pairs = File.readlines(file).map {|x| x.chomp.split }
168
+ @api.setvars(pairs)
164
169
  end
165
170
 
166
171
  def set_variables(pairs)
167
- pairs.each do |pair|
168
- var, value = *pair
169
- @parent.setvar(var, value)
170
- end
172
+ @api.setvars(pairs)
171
173
  end
172
174
 
173
175
  def grab_file(fname)
@@ -217,26 +219,27 @@ module Livetext::Helpers
217
219
  end
218
220
 
219
221
  def setvar(var, val)
220
- str, sym = var.to_s, var.to_sym
221
- Livetext::Vars[str] = val
222
- Livetext::Vars[sym] = val
223
- @_vars[str] = val
224
- @_vars[sym] = val
222
+ api.setvar(var, val)
223
+ # str, sym = var.to_s, var.to_sym
224
+ # Livetext::Vars[str] = val
225
+ # Livetext::Vars[sym] = val
226
+ # @_vars[str] = val
227
+ # @_vars[sym] = val
225
228
  end
226
229
 
227
230
  def setfile(file)
228
231
  if file
229
- setvar(:File, file)
232
+ api.setvar(:File, file)
230
233
  dir = File.dirname(File.expand_path(file))
231
- setvar(:FileDir, dir)
234
+ api.setvar(:FileDir, dir)
232
235
  else
233
- setvar(:File, "[no file]")
234
- setvar(:FileDir, "[no dir]")
236
+ api.setvar(:File, "[no file]")
237
+ api.setvar(:FileDir, "[no dir]")
235
238
  end
236
239
  end
237
240
 
238
241
  def setfile!(file) # FIXME why does this variant exist?
239
- setvar(:File, file)
242
+ api.setvar(:File, file)
240
243
  end
241
244
 
242
245
  end
data/lib/livetext/html.rb CHANGED
@@ -29,5 +29,78 @@ module HTMLHelper
29
29
  end
30
30
  [open, close]
31
31
  end
32
+ end
33
+
34
+ class HTML
35
+
36
+ def initialize(api)
37
+ @api = api
38
+ @indent = 0
39
+ end
40
+
41
+ def indented
42
+ " "*@indent
43
+ end
44
+
45
+ def indent(which)
46
+ case which
47
+ when :in, :right
48
+ @indent += 2
49
+ when :out, :left
50
+ @indent -= 2
51
+ else
52
+ abort "indent(#{which}) is nonsense"
53
+ end
54
+ end
55
+
56
+ def nav(**details, &block)
57
+ wrap(:nav, **details, &block)
58
+ end
32
59
 
60
+ def div(**details, &block)
61
+ wrap(:div, **details, &block)
62
+ end
63
+
64
+ def ul(**details, &block)
65
+ wrap(:ul, **details, &block)
66
+ end
67
+
68
+ def li(**details, &block)
69
+ wrap(:li, **details, &block)
70
+ end
71
+
72
+ def api
73
+ @api
74
+ end
75
+
76
+ def open_close_tags(*tags)
77
+ open, close = "", ""
78
+ tags.each do |tag|
79
+ open << "<#{tag}>"
80
+ close.prepend("</#{tag}>")
81
+ end
82
+ [open, close]
83
+ end
84
+
85
+ def wrap(*tags, **extras) # helper
86
+ open, close = open_close_tags(*tags)
87
+ extras.each_pair do |name, value|
88
+ open.sub!(">", " #{name}='#{value}'>")
89
+ end
90
+ api.out indented + open
91
+ indent(:in)
92
+ yield
93
+ indent(:out)
94
+ api.out indented + close
95
+ end
96
+
97
+ def tag(*tags, cdata: "", **extras) # helper
98
+ open, close = open_close_tags(*tags)
99
+ extras.each_pair do |name, value|
100
+ open.sub!(">", " #{name}='#{value}'>")
101
+ end
102
+ str = indented + open + cdata + close
103
+ str
104
+ end
33
105
  end
106
+
@@ -0,0 +1,178 @@
1
+
2
+ # Class Livetext reopened (top level).
3
+
4
+ class Livetext
5
+
6
+ include Helpers
7
+
8
+ class Variables
9
+ attr_reader :vars
10
+ def initialize(hash = {})
11
+ @vars = {}
12
+ hash.each_pair {|k, v| @vars[k.to_sym] = v }
13
+ end
14
+
15
+ def [](var)
16
+ @vars[var.to_sym]
17
+ end
18
+
19
+ def []=(var, value)
20
+ @vars[var.to_sym] = value
21
+ end
22
+
23
+ def get(var)
24
+ @vars[var.to_sym] || "[#{var} is undefined]"
25
+ end
26
+
27
+ def set(var, value)
28
+ @vars[var.to_sym] = value.to_s
29
+ end
30
+
31
+ def setvars(pairs)
32
+ pairs = pairs.to_a if pairs.is_a?(Hash)
33
+ pairs.each do |var, value|
34
+ api.setvar(var, value)
35
+ end
36
+ end
37
+ end
38
+
39
+ Vars = Variables.new
40
+
41
+ TTY = ::File.open("/dev/tty", "w")
42
+
43
+ attr_reader :main, :sources
44
+ attr_accessor :nopass, :nopara
45
+ attr_accessor :body, :indentation
46
+
47
+ class << self
48
+ attr_accessor :output # bad solution?
49
+ end
50
+
51
+ def vars
52
+ @_vars
53
+ end
54
+
55
+ def self.interpolate(str)
56
+ expand = Livetext::Expansion.new(self)
57
+ str2 = expand.expand_variables(str)
58
+ str3 = expand.expand_function_calls(str2)
59
+ str3
60
+ end
61
+
62
+ def self.customize(mix: [], call: [], vars: {})
63
+ obj = self.new
64
+ mix = Array(mix)
65
+ call = Array(call)
66
+ mix.each {|lib| obj.mixin(lib) }
67
+ call.each {|cmd| obj.main.send(cmd[1..-1]) } # ignores leading dot, no param
68
+ # vars.each_pair {|var, val| obj.setvar(var, val.to_s) }
69
+ api.setvars(vars)
70
+ obj
71
+ end
72
+
73
+ def peek_nextline
74
+ @main.peek_nextline # delegate
75
+ end
76
+
77
+ def nextline
78
+ @main.nextline # delegate
79
+ end
80
+
81
+ def sources
82
+ @main.sources # delegate
83
+ end
84
+
85
+ def save_location
86
+ @save_location # delegate
87
+ end
88
+
89
+ def save_location=(where)
90
+ @save_location = where # delegate
91
+ end
92
+
93
+ def dump(file = nil) # not a dot command!
94
+ file ||= ::STDOUT
95
+ file.puts @body
96
+ rescue => err
97
+ TTY.puts "#dump had an error: #{err.inspect}"
98
+ end
99
+
100
+ def graceful_error(err)
101
+ dump
102
+ raise err
103
+ end
104
+
105
+ def customize(mix: [], call: [], vars: {})
106
+ mix = Array(mix)
107
+ call = Array(call)
108
+ mix.each {|lib| mixin(lib) }
109
+ call.each {|cmd| @main.send(cmd[1..-1]) } # ignores leading dot, no param
110
+ # vars.each_pair {|var, val| @api.set(var, val.to_s) }
111
+ api.setvars(vars)
112
+ self
113
+ end
114
+
115
+ def initialize(output = ::STDOUT)
116
+ @source = nil
117
+ @_mixins = []
118
+ @_imports = []
119
+ @_outdir = "."
120
+ @no_puts = output.nil?
121
+ @body = ""
122
+ @main = Processor.new(self, output)
123
+ @indentation = [0]
124
+ @_vars = Livetext::Vars
125
+ @api = UserAPI.new(self)
126
+ initial_vars
127
+ end
128
+
129
+ def api
130
+ @api
131
+ end
132
+
133
+ def initial_vars
134
+ # Other predefined variables (see also setfile)
135
+ @api.setvar(:User, `whoami`.chomp)
136
+ @api.setvar(:Version, Livetext::VERSION)
137
+ end
138
+
139
+ def transform(text)
140
+ setfile!("(string)")
141
+ enum = text.each_line
142
+ front = text.match(/.*?\n/).to_a.first.chomp rescue ""
143
+ @main.source(enum, "STDIN: '#{front}...'", 0)
144
+ loop do
145
+ line = @main.nextline
146
+ break if line.nil?
147
+ process_line(line)
148
+ end
149
+ result = @body
150
+ # @body = ""
151
+ result
152
+ end
153
+
154
+ # EXPERIMENTAL and incomplete
155
+ def xform(*args, file: nil, text: nil, vars: {})
156
+ case
157
+ when file && text.nil?
158
+ xform_file(file)
159
+ when file.nil? && text
160
+ transform(text)
161
+ when file.nil? && text.nil?
162
+ raise "Must specify file or text"
163
+ when file && text
164
+ raise "Cannot specify file and text"
165
+ end
166
+ self.process_file(file)
167
+ self.body
168
+ end
169
+
170
+ def xform_file(file, vars: nil)
171
+ Livetext::Vars.replace(vars) unless vars.nil?
172
+ @_vars.replace(vars) unless vars.nil?
173
+ self.process_file(file)
174
+ self.body
175
+ end
176
+
177
+ end
178
+