livetext 0.9.09 → 0.9.14

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.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/bin/livetext +0 -1
  3. data/lib/errors.rb +15 -0
  4. data/lib/formatline.rb +3 -5
  5. data/lib/functions.rb +6 -2
  6. data/lib/helpers.rb +25 -0
  7. data/lib/html.rb +32 -0
  8. data/lib/livetext/importable.rb +2 -0
  9. data/lib/livetext.rb +60 -48
  10. data/lib/parser/general.rb +38 -0
  11. data/lib/parser/import.rb +17 -0
  12. data/lib/parser/mixin.rb +40 -0
  13. data/lib/parser/set.rb +136 -0
  14. data/lib/parser/string.rb +55 -0
  15. data/lib/parser.rb +5 -0
  16. data/lib/processor.rb +22 -23
  17. data/lib/standard.rb +145 -298
  18. data/lib/userapi.rb +2 -5
  19. data/livetext.gemspec +1 -2
  20. data/test/all.rb +4 -0
  21. data/test/formatting-tests.rb +35 -0
  22. data/test/formatting.rb +2 -9
  23. data/test/snapshots/OMIT.txt +10 -0
  24. data/test/{data → snapshots}/basic_formatting/expected-error.txt +0 -0
  25. data/test/{data → snapshots}/basic_formatting/expected-output.txt +0 -0
  26. data/test/{data → snapshots}/basic_formatting/source.lt3 +0 -0
  27. data/test/{data → snapshots}/block_comment/expected-error.txt +0 -0
  28. data/test/{data → snapshots}/block_comment/expected-output.txt +0 -0
  29. data/test/{data → snapshots}/block_comment/source.lt3 +0 -0
  30. data/test/{data → snapshots}/comments_ignored_1/expected-error.txt +0 -0
  31. data/test/{data → snapshots}/comments_ignored_1/expected-output.txt +0 -0
  32. data/test/{data → snapshots}/comments_ignored_1/source.lt3 +0 -0
  33. data/test/{data → snapshots}/copy_is_raw/expected-error.txt +0 -0
  34. data/test/{data → snapshots}/copy_is_raw/expected-output.txt +0 -0
  35. data/test/{data → snapshots}/copy_is_raw/rawtext.inc +0 -0
  36. data/test/{data → snapshots}/copy_is_raw/source.lt3 +0 -0
  37. data/test/{data → snapshots}/crap +0 -0
  38. data/test/{data → snapshots}/def_method/expected-error.txt +0 -0
  39. data/test/{data → snapshots}/def_method/expected-output.txt +0 -0
  40. data/test/{data → snapshots}/def_method/source.lt3 +0 -0
  41. data/test/{data → snapshots}/error_inc_line_num/expected-output.txt +6 -0
  42. data/test/{data → snapshots}/error_inc_line_num/file2.lt3 +0 -0
  43. data/test/snapshots/error_inc_line_num/match-error.txt +1 -0
  44. data/test/{data → snapshots}/error_inc_line_num/source.lt3 +0 -0
  45. data/test/{data → snapshots}/error_invalid_name/expected-output.txt +0 -0
  46. data/test/snapshots/error_invalid_name/match-error.txt +1 -0
  47. data/test/{data → snapshots}/error_invalid_name/source.lt3 +0 -0
  48. data/test/{data → snapshots}/error_line_num/expected-output.txt +0 -0
  49. data/test/snapshots/error_line_num/match-error.txt +1 -0
  50. data/test/{data → snapshots}/error_line_num/source.lt3 +0 -0
  51. data/test/{data → snapshots}/error_mismatched_end/expected-output.txt +0 -0
  52. data/test/snapshots/error_mismatched_end/match-error.txt +1 -0
  53. data/test/{data → snapshots}/error_mismatched_end/source.lt3 +0 -0
  54. data/test/{data → snapshots}/error_missing_end/expected-output.txt +1 -0
  55. data/test/snapshots/error_missing_end/match-error.txt +1 -0
  56. data/test/{data → snapshots}/error_missing_end/source.lt3 +0 -0
  57. data/test/{data/error_no_such_mixin → snapshots/error_name_not_permitted}/expected-output.txt +0 -0
  58. data/test/snapshots/error_name_not_permitted/match-error.txt +1 -0
  59. data/test/{data → snapshots}/error_name_not_permitted/source.lt3 +0 -0
  60. data/test/{data → snapshots}/error_no_such_copy/expected-output.txt +0 -1
  61. data/test/snapshots/error_no_such_copy/match-error.txt +1 -0
  62. data/test/{data → snapshots}/error_no_such_copy/source.lt3 +1 -0
  63. data/test/{data → snapshots}/error_no_such_inc/expected-output.txt +0 -0
  64. data/test/snapshots/error_no_such_inc/match-error.txt +1 -0
  65. data/test/{data → snapshots}/error_no_such_inc/source.lt3 +0 -0
  66. data/test/snapshots/error_no_such_mixin/expected-output.txt +5 -0
  67. data/test/snapshots/error_no_such_mixin/match-error.txt +1 -0
  68. data/test/{data → snapshots}/error_no_such_mixin/source.lt3 +0 -0
  69. data/test/{data → snapshots}/example_alpha/expected-error.txt +0 -0
  70. data/test/{data → snapshots}/example_alpha/expected-output.txt +0 -0
  71. data/test/{data → snapshots}/example_alpha/source.lt3 +0 -0
  72. data/test/{data → snapshots}/example_alpha2/expected-error.txt +0 -0
  73. data/test/{data → snapshots}/example_alpha2/expected-output.txt +0 -0
  74. data/test/{data → snapshots}/example_alpha2/source.lt3 +0 -0
  75. data/test/{data → snapshots}/fixit +0 -0
  76. data/test/{data/lines.txt → snapshots/formatting-tests.txt} +4 -0
  77. data/test/{data → snapshots}/functions/expected-error.txt +0 -0
  78. data/test/{data → snapshots}/functions/expected-output.txt +0 -0
  79. data/test/{data → snapshots}/functions/source.lt3 +0 -0
  80. data/test/{data → snapshots}/hello_world/expected-error.txt +0 -0
  81. data/test/{data → snapshots}/hello_world/expected-output.txt +0 -0
  82. data/test/{data → snapshots}/hello_world/source.lt3 +0 -0
  83. data/test/{data → snapshots}/more_complex_vars/expected-error.txt +0 -0
  84. data/test/{data → snapshots}/more_complex_vars/expected-output.txt +0 -0
  85. data/test/{data → snapshots}/more_complex_vars/source.lt3 +0 -0
  86. data/test/{data/raw_lines → snapshots/predef_vars}/expected-error.txt +0 -0
  87. data/test/snapshots/predef_vars/match-output.txt +6 -0
  88. data/test/snapshots/predef_vars/source.lt3 +6 -0
  89. data/test/{data/raw_text_block → snapshots/raw_lines}/expected-error.txt +0 -0
  90. data/test/{data → snapshots}/raw_lines/expected-output.txt +0 -0
  91. data/test/{data → snapshots}/raw_lines/source.lt3 +0 -0
  92. data/test/{data/simple_copy → snapshots/raw_text_block}/expected-error.txt +0 -0
  93. data/test/{data → snapshots}/raw_text_block/expected-output.txt +0 -0
  94. data/test/{data → snapshots}/raw_text_block/rawtext.inc +0 -0
  95. data/test/{data → snapshots}/raw_text_block/source.lt3 +0 -0
  96. data/test/{data/simple_include → snapshots/simple_copy}/expected-error.txt +0 -0
  97. data/test/{data → snapshots}/simple_copy/expected-output.txt +0 -0
  98. data/test/{data → snapshots}/simple_copy/simplefile.inc +0 -0
  99. data/test/{data → snapshots}/simple_copy/source.lt3 +0 -0
  100. data/test/{data/simple_mixin → snapshots/simple_include}/expected-error.txt +0 -0
  101. data/test/{data → snapshots}/simple_include/expected-output.txt +0 -0
  102. data/test/{data → snapshots}/simple_include/simplefile.inc +0 -0
  103. data/test/{data → snapshots}/simple_include/source.lt3 +0 -0
  104. data/test/{data/simple_vars → snapshots/simple_mixin}/expected-error.txt +0 -0
  105. data/test/{data → snapshots}/simple_mixin/expected-output.txt +0 -0
  106. data/test/{data → snapshots}/simple_mixin/simple_mixin.rb +0 -0
  107. data/test/{data → snapshots}/simple_mixin/source.lt3 +0 -0
  108. data/test/{data/single_raw_line → snapshots/simple_vars}/expected-error.txt +0 -0
  109. data/test/{data → snapshots}/simple_vars/expected-output.txt +0 -0
  110. data/test/{data → snapshots}/simple_vars/source.lt3 +0 -0
  111. data/test/{data/table_with_heredocs → snapshots/single_raw_line}/expected-error.txt +0 -0
  112. data/test/{data → snapshots}/single_raw_line/expected-output.txt +0 -0
  113. data/test/{data → snapshots}/single_raw_line/source.lt3 +0 -0
  114. data/test/{data → snapshots}/subset.txt +0 -0
  115. data/test/snapshots/table_with_heredocs/expected-error.txt +0 -0
  116. data/test/{data → snapshots}/table_with_heredocs/expected-output.txt +0 -0
  117. data/test/{data → snapshots}/table_with_heredocs/source.lt3 +0 -0
  118. data/test/snapshots.rb +168 -0
  119. data/test/testlines.rb +17 -7
  120. data/test/unit/all.rb +3 -0
  121. data/test/unit/html.rb +38 -0
  122. data/test/unit/parser/all.rb +3 -0
  123. data/test/unit/parser/general.rb +59 -0
  124. data/test/unit/parser/importable.rb +19 -0
  125. data/test/unit/parser/mixin.rb +19 -0
  126. data/test/unit/parser/set.rb +157 -0
  127. data/test/unit/parser/string.rb +130 -0
  128. data/test/unit/parser.rb +6 -0
  129. data/test/unit/standard.rb +23 -0
  130. data/test/unit/stringparser.rb +140 -0
  131. metadata +122 -96
  132. data/test/data/error_inc_line_num/expected-err-line1match.txt +0 -1
  133. data/test/data/error_invalid_name/expected-err-line1match.txt +0 -1
  134. data/test/data/error_line_num/expected-err-line1match.txt +0 -1
  135. data/test/data/error_mismatched_end/expected-err-line1match.txt +0 -1
  136. data/test/data/error_missing_end/expected-err-line1match.txt +0 -1
  137. data/test/data/error_name_not_permitted/expected-error.txt +0 -1
  138. data/test/data/error_name_not_permitted/expected-output.txt +0 -4
  139. data/test/data/error_no_such_copy/expected-err-line1match.txt +0 -1
  140. data/test/data/error_no_such_inc/expected-err-line1match.txt +0 -1
  141. data/test/data/error_no_such_mixin/expected-err-line1match.txt +0 -1
  142. data/test/extratests.txt +0 -20
  143. data/test/test.rb +0 -140
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5b79c9b4bec7d50d6514a46d2d4b459cb769748316a1aa3c124943292862e2d
4
- data.tar.gz: 970dfa9d9838a555619491723c4ddacd62764767bcbe427dd407d2f541c7c887
3
+ metadata.gz: d9e6ad9c8d933b361da03fadc6cc4d28e0a8b699f8f50e434db9bf41f0b9d66b
4
+ data.tar.gz: 49e59704162aea72ac92122d69f0006591856b9fe09f2b2fdbf6843b7253b80b
5
5
  SHA512:
6
- metadata.gz: 7f8402adab86a07e37c3e663de62bc2681c84cfe4bc0fb0791e6a2413c2f94991fe861712183c7e82061cd8134a1d6ae6fbc225d90ed57eb78f3cf09a42e5731
7
- data.tar.gz: 4f8a97b146ca5e73c65a01681ee7871e198e0d49fa94866122d06dff35f0266cac54b24ae7d559020210ba49479ed041836d8e3e3a1b150a669e54236d8aeb56
6
+ metadata.gz: 74b1b06e0bc91d976c0f60a2136ed8bb1c4d2d5ba33359620752b44a51918a89fbba2cec9c542ae12a6368de82bdc4db64b30a4d95146caab4e5a290f9c7fa8b
7
+ data.tar.gz: ed0c0fa3b4da538ddbd31cd799a84f5647d45a369f0f45d729c223f74c7b0f2445bfc0812aa0c319b9a9f5992efa6af521d550eed38a8e2c551b183e28a16722
data/bin/livetext CHANGED
@@ -72,7 +72,6 @@ loop do
72
72
  @backtrace = true
73
73
  when "-m", "--mixin"
74
74
  mod = ARGV.shift
75
- STDERR.puts "Got mod: #{mod}"
76
75
  x.mixin(ARGV.shift)
77
76
  next
78
77
  when "-s", "--stdin"
data/lib/errors.rb ADDED
@@ -0,0 +1,15 @@
1
+
2
+ # More later?
3
+
4
+ def make_exception(sym, str, target_class = Object)
5
+ return if target_class.constants.include?(sym)
6
+ klass = sym # :"#{sym}_Class"
7
+ target_class.const_set(klass, StandardError.dup)
8
+ define_method(sym) do |*args|
9
+ args = [] unless args.first
10
+ msg = str.dup
11
+ args.each.with_index {|arg, i| msg.sub!("%#{i+1}", arg) }
12
+ target_class.class_eval(klass.to_s).new(msg)
13
+ end
14
+ end
15
+
data/lib/formatline.rb CHANGED
@@ -1,3 +1,6 @@
1
+ # Class FormatLine handles the parsing of comments, dot commands, and
2
+ # simple formatting characters.
3
+
1
4
  class FormatLine
2
5
  SimpleFormats = {}
3
6
  SimpleFormats[:b] = %w[<b> </b>]
@@ -395,11 +398,6 @@ class FormatLine
395
398
 
396
399
  #####
397
400
 
398
- def showme(tag)
399
- char = @line[@cc]
400
- puts "--- #{tag}: ch=#{@ch.inspect} next=#{@next.inspect} (cc=#@cc:#{char.inspect}) out=#{@out.inspect}"
401
- end
402
-
403
401
  def embedded?
404
402
  ! (['"', "'", " ", nil].include? prev)
405
403
  end
data/lib/functions.rb CHANGED
@@ -1,6 +1,10 @@
1
- require 'standard' # FIXME
2
1
 
3
- class Livetext::Functions # Functions will go here... user-def AND pre-def??
2
+ require_relative 'standard' # FIXME umm, why is this necessary??
3
+
4
+ # Class Functions is where '$$func' functions are stored dynamically...
5
+ # user-def AND pre-def??
6
+
7
+ class Livetext::Functions
4
8
  Formats = ::Livetext::Standard::SimpleFormats
5
9
 
6
10
  @param = nil
data/lib/helpers.rb ADDED
@@ -0,0 +1,25 @@
1
+
2
+ module Helpers
3
+
4
+ def check_disallowed(name)
5
+ # raise "Illegal name '#{name}'" if _disallowed?(name)
6
+ # FIXME use custom exception
7
+ raise DisallowedName, name if _disallowed?(name)
8
+ end
9
+
10
+ def check_file_exists(file)
11
+ raise FileNotFound(file) unless File.exist?(file)
12
+ end
13
+
14
+ def set_variables(pairs)
15
+ pairs.each do |pair|
16
+ var, value = *pair
17
+ @parent._setvar(var, value)
18
+ end
19
+ end
20
+
21
+ def grab_file(fname)
22
+ File.read(fname)
23
+ end
24
+
25
+ end
data/lib/html.rb ADDED
@@ -0,0 +1,32 @@
1
+ module HTMLHelper
2
+
3
+ def wrapped(str, *tags) # helper
4
+ open, close = open_close_tags(*tags)
5
+ open + str + close
6
+ end
7
+
8
+ def wrapped!(str, tag, **extras) # helper
9
+ open, close = open_close_tags(tag)
10
+ extras.each_pair do |name, value|
11
+ open.sub!(">", " #{name}='#{value}'>")
12
+ end
13
+ open + str + close
14
+ end
15
+
16
+ def wrap(*tags) # helper
17
+ open, close = open_close_tags(*tags)
18
+ _out open
19
+ yield
20
+ _out close
21
+ end
22
+
23
+ def open_close_tags(*tags)
24
+ open, close = "", ""
25
+ tags.each do |tag|
26
+ open << "<#{tag}>"
27
+ close.prepend("</#{tag}>")
28
+ end
29
+ [open, close]
30
+ end
31
+
32
+ end
@@ -0,0 +1,2 @@
1
+ class LiveText::Importable
2
+ end
data/lib/livetext.rb CHANGED
@@ -1,34 +1,53 @@
1
+ # Class Livetext skeleton (top level).
2
+
1
3
  class Livetext
2
- VERSION = "0.9.09"
4
+ VERSION = "0.9.14"
3
5
  Path = File.expand_path(File.join(File.dirname(__FILE__)))
4
6
  end
5
7
 
6
- require 'fileutils'
8
+ # $LOAD_PATH << Livetext::Path
7
9
 
8
- $: << Livetext::Path
10
+ require 'fileutils'
9
11
 
10
- require 'functions'
11
- require 'userapi'
12
- require 'standard'
13
- require 'formatline'
14
- require 'processor'
12
+ require 'helpers'
13
+ require_relative 'errors'
14
+ require_relative 'functions'
15
+ require_relative 'userapi'
16
+ require_relative 'standard'
17
+ require_relative 'formatline'
18
+ require_relative 'processor'
15
19
 
16
20
  Plugins = File.expand_path(File.join(File.dirname(__FILE__), "../plugin"))
17
21
 
18
22
  TTY = ::File.open("/dev/tty", "w")
19
23
 
24
+ make_exception(:EndWithoutOpening, "Error: found .end with no opening command")
25
+
26
+ # Class Livetext reopened (top level).
27
+
20
28
  class Livetext
29
+
30
+ include Helpers
31
+
21
32
  Vars = {}
22
33
 
34
+ Space = " "
35
+ Sigil = "." # Can't change yet
36
+
37
+ def self.rx(str, space=nil)
38
+ Regexp.compile("^" + Regexp.escape(str) + "#{space}")
39
+ end
40
+
41
+ Comment = rx(Sigil, Livetext::Space)
42
+ Dotcmd = rx(Sigil)
43
+ Ddotcmd = /^ *\$\.[A-Za-z]/
44
+
23
45
  attr_reader :main
24
46
  attr_accessor :no_puts
25
47
  attr_accessor :body, :indentation
26
48
 
27
- # FIXME - phase out stupid 'parameters' method
28
-
29
49
  class << self
30
- attr_accessor :parameters # from outside world (process_text)
31
- attr_accessor :output # both bad solutions?
50
+ attr_accessor :output # bad solution?
32
51
  end
33
52
 
34
53
  def vars
@@ -54,17 +73,6 @@ class Livetext
54
73
  self
55
74
  end
56
75
 
57
- Space = " "
58
- Sigil = "." # Can't change yet
59
-
60
- def self.rx(str, space=nil)
61
- Regexp.compile("^" + Regexp.escape(str) + "#{space}")
62
- end
63
-
64
- Comment = rx(Sigil, Livetext::Space)
65
- Dotcmd = rx(Sigil)
66
- Ddotcmd = /^ *\$\.[A-Za-z]/
67
-
68
76
  def initialize(output = ::STDOUT)
69
77
  @source = nil
70
78
  @_mixins = []
@@ -74,6 +82,10 @@ class Livetext
74
82
  @main = Processor.new(self, output)
75
83
  @indentation = [0]
76
84
  @_vars = Livetext::Vars
85
+
86
+ # Other predefined variables (see also _setfile)
87
+ _setvar(:User, `whoami`.chomp)
88
+ _setvar(:Version, Livetext::VERSION)
77
89
  end
78
90
 
79
91
  def _parse_colon_args(args, hash) # really belongs in livetext
@@ -111,9 +123,14 @@ class Livetext
111
123
  end
112
124
 
113
125
  def _setfile(file)
114
- _setvar(:File, file)
115
- dir = File.dirname(File.expand_path(file))
116
- _setvar(:FileDir, dir)
126
+ if file
127
+ _setvar(:File, file)
128
+ dir = File.dirname(File.expand_path(file))
129
+ _setvar(:FileDir, dir)
130
+ else
131
+ _setvar(:File, "[no file]")
132
+ _setvar(:FileDir, "[no dir]")
133
+ end
117
134
  end
118
135
 
119
136
  def _setfile!(file)
@@ -126,7 +143,6 @@ class Livetext
126
143
  when Comment
127
144
  handle_scomment(line)
128
145
  when Dotcmd
129
- STDERR.puts "line = #{line.inspect}"
130
146
  handle_dotcmd(line)
131
147
  when Ddotcmd
132
148
  indent = line.index("$") + 1
@@ -170,7 +186,7 @@ class Livetext
170
186
  self.body
171
187
  end
172
188
 
173
- def xform_file(file) # , vars: {})
189
+ def xform_file(file, vars: nil)
174
190
  Livetext::Vars.replace(vars) unless vars.nil?
175
191
  @_vars.replace(vars) unless vars.nil?
176
192
  self.process_file(file)
@@ -193,28 +209,23 @@ class Livetext
193
209
  end
194
210
  val = @main.finalize if @main.respond_to? :finalize
195
211
  @body
196
- rescue => err
197
- STDERR.puts "[process_file] fname = #{fname.inspect}\n line = #{line.inspect}"
198
- STDERR.puts "ERROR #{err} in process_file"
199
- err.backtrace.each {|x| STDERR.puts " " + x }
200
- @body = ""
212
+ rescue => err
213
+ STDERR.puts "[process_file] fname = #{fname.inspect}\n line = #{line.inspect}"
214
+ STDERR.puts "ERROR #{err} in process_file"
215
+ err.backtrace.each {|x| STDERR.puts " " + x }
216
+ # @body = ""
201
217
  end
202
218
 
203
219
  def handle_scomment(line)
204
220
  end
205
221
 
206
- def _check_name(name)
207
- @main._error! "Name '#{name}' is not permitted" if @main._disallowed?(name)
208
- @main._error! "Mismatched 'end'" if name == "end"
209
- name = "_" + name if %w[def include].include?(name)
210
- name
211
- end
212
-
213
222
  def _get_name(line)
214
223
  name, data = line.split(" ", 2)
215
224
  name = name[1..-1] # chop off sigil
225
+ name = "_" + name if %w[include def].include?(name)
216
226
  @main.data = data
217
- name = _check_name(name)
227
+ @main.check_disallowed(name)
228
+ name
218
229
  end
219
230
 
220
231
  def handle_dotcmd(line, indent = 0)
@@ -222,20 +233,21 @@ class Livetext
222
233
  line = line.sub(/# .*$/, "")
223
234
  name = _get_name(line).to_sym
224
235
  result = nil
225
- if @main.respond_to?(name)
226
- result = @main.send(name)
236
+ case
237
+ when name == :end # special case
238
+ puts @body
239
+ raise EndWithoutOpening()
240
+ when @main.respond_to?(name)
241
+ result = @main.send(name)
227
242
  else
228
243
  @main._error! "Name '#{name}' is unknown"
229
244
  return
230
245
  end
231
246
  result
232
247
  rescue => err
233
- STDERR.puts "Error was: #{err.inspect} (calling @main._error!)"
248
+ puts @body # earlier correct output, not flushed yet
249
+ STDERR.puts "Error: #{err.inspect}"
234
250
  STDERR.puts err.backtrace
235
- @main._error!(err)
236
- # puts @body
237
- @body = ""
238
- return @body
239
251
  end
240
252
 
241
253
  end
@@ -0,0 +1,38 @@
1
+
2
+ require_relative '../livetext'
3
+ require_relative 'string'
4
+
5
+ make_exception(:MismatchedQuotes, "Error: mismatched quotes")
6
+ make_exception(:NilValue, "Error: nil value")
7
+ make_exception(:NullString, "Error: null string")
8
+ make_exception(:ExpectedString, "Error: expected a string")
9
+
10
+ class Livetext::ParseGeneral < StringParser
11
+
12
+ def initialize(str)
13
+ super
14
+ end
15
+
16
+ def strip_quotes
17
+ raise NullString if @line.empty?
18
+ start, stop = @line[0], @line[-1]
19
+ return @line unless %['"].include?(start)
20
+ raise MismatchedQuotes if start != stop
21
+ @line[1..-2]
22
+ end
23
+
24
+ def self.parse_vars(lines, prefix: nil)
25
+ lines.map! {|line| line.sub(/# .*/, "").strip } # strip comments
26
+ pairs = []
27
+ lines.each do |line|
28
+ next if line.strip.empty?
29
+ var, value = line.split(" ", 2)
30
+ val = FormatLine.var_func_parse(value)
31
+ var = prefix + "." + var if prefix
32
+ pairs << [var, value]
33
+ end
34
+ pairs
35
+ end
36
+
37
+ end
38
+
@@ -0,0 +1,17 @@
1
+
2
+ require '../livetext/importable'
3
+
4
+ make_exception(:BadVariableName, "Error: invalid variable name")
5
+ make_exception(:NoEqualSign, "Error: no equal sign found")
6
+
7
+ # FIXME probably belongs elsewhere?
8
+
9
+ class Livetext::ParseImport
10
+ def use_import(name)
11
+ require name
12
+ include name
13
+ init = "init_#{name}"
14
+ self.send(init) if self.respond_to? init
15
+ end
16
+ end
17
+
@@ -0,0 +1,40 @@
1
+ require_relative '../livetext'
2
+ require_relative 'string'
3
+
4
+ make_exception(:BadVariableName, "Error: invalid variable name")
5
+ make_exception(:NoEqualSign, "Error: no equal sign found")
6
+
7
+ # FIXME probably belongs elsewhere?
8
+
9
+ class Livetext::ParseMixin # < StringParser
10
+
11
+ include Helpers
12
+
13
+ def cwd_root?
14
+ File.dirname(File.expand_path(".")) == "/"
15
+ end
16
+
17
+ def find_mixin(name)
18
+ file = "#{Plugins}/" + name.downcase + ".rb"
19
+ return file if File.exist?(file)
20
+
21
+ file = "./#{name}.rb"
22
+ return file if File.exist?(file)
23
+
24
+ raise "No such mixin '#{name}'" if cwd_root?
25
+ Dir.chdir("..") { find_mixin(name) }
26
+ end
27
+
28
+ def use_mixin(name, file)
29
+ modname = name.gsub("/","_").capitalize
30
+ meths = grab_file(file)
31
+ string = "module ::#{modname}; #{meths}\nend"
32
+ eval(string)
33
+ newmod = Object.const_get("::" + modname)
34
+ self.extend(newmod)
35
+ init = "init_#{name}"
36
+ self.send(init) if self.respond_to? init
37
+ end
38
+
39
+ end
40
+
data/lib/parser/set.rb ADDED
@@ -0,0 +1,136 @@
1
+
2
+ require_relative '../livetext'
3
+ require_relative 'string'
4
+
5
+ make_exception(:BadVariableName, "Error: invalid variable name")
6
+ make_exception(:NoEqualSign, "Error: no equal sign found")
7
+
8
+ class Livetext::ParseSet < StringParser
9
+
10
+ attr_reader :line, :eos, :i, :len
11
+
12
+ def self.parse(str)
13
+ self.new(str).parse
14
+ end
15
+
16
+ def initialize(line)
17
+ super
18
+ end
19
+
20
+ def parse
21
+ pairs = []
22
+
23
+ loop do
24
+ skip_spaces
25
+ char = self.peek
26
+ break if eos? # end of string
27
+ raise "Expected alpha to start var name" unless char =~ /[a-z]/i
28
+ pairs << assignment
29
+ skip_spaces
30
+ char = self.peek
31
+ break if eos? # end of string
32
+ case char
33
+ when nil # end of string
34
+ when ","
35
+ self.next # skip comma
36
+ else
37
+ raise "Expected comma or end of string (found #{char.inspect})"
38
+ end
39
+ end
40
+ pairs
41
+ end
42
+
43
+ def assignment # one single var=value
44
+ pair = nil
45
+ var = value = nil
46
+ return if eos?
47
+ var = get_var
48
+ skip_equal
49
+ value = get_value
50
+ value = FormatLine.var_func_parse(value)
51
+ pair = [var, value]
52
+ pair
53
+ end
54
+
55
+ def get_var
56
+ name = ""
57
+ loop do
58
+ char = self.peek
59
+ break if eos? # end of string
60
+ case char
61
+ when /[a-zA-Z_\.0-9]/
62
+ name << self.next
63
+ next
64
+ when /[ =]/
65
+ return name
66
+ else
67
+ raise BadVariableName, char, name
68
+ end
69
+ end
70
+ raise NoEqualSign
71
+ end
72
+
73
+ def skip_equal
74
+ found = false
75
+ skip_spaces
76
+ raise NoEqualSign unless self.peek == "="
77
+ found = true
78
+ self.next # skip =... spaces too
79
+ self.skip_spaces
80
+ peek = self.peek
81
+ return peek # just for testing
82
+ rescue StopIteration
83
+ raise NoEqualSign unless found
84
+ return nil
85
+ end
86
+
87
+ def escaped
88
+ self.next # skip backslash
89
+ self.next # return following char
90
+ end
91
+
92
+ make_exception(:BadQuotedString, "Bad quoted string: %1")
93
+
94
+ def quoted_value
95
+ quote = self.next # opening quote...
96
+ value = ""
97
+ char = nil
98
+ loop do
99
+ char = self.peek
100
+ break if self.eos?
101
+ break if char == quote
102
+ char = escaped if char == "\\"
103
+ value << char
104
+ char = self.next
105
+ end
106
+ if char == quote
107
+ char = self.next
108
+ return value
109
+ end
110
+ raise BadQuotedString, quote + value
111
+ end
112
+
113
+ def unquoted_value
114
+ # puts "#{__method__}: #{@line.inspect} i = #@i peek = #{self.peek.inspect}"
115
+ value = ""
116
+ loop do
117
+ char = self.peek
118
+ break if self.eos?
119
+ break if char == " " || char == ","
120
+ value << char
121
+ char = self.next
122
+ end
123
+ value
124
+ end
125
+
126
+ def quote?(char)
127
+ char == ?" || char == ?'
128
+ end
129
+
130
+ def get_value
131
+ char = self.peek
132
+ value = quote?(char) ? quoted_value : unquoted_value
133
+ value
134
+ end
135
+ end
136
+
@@ -0,0 +1,55 @@
1
+ class StringParser
2
+
3
+ attr_reader :line, :eos, :i, :len
4
+
5
+ def initialize(line)
6
+ raise NilValue if line.nil?
7
+ raise ExpectedString unless String === line
8
+ # raise NullString if line.empty?
9
+ @line = line
10
+ @len = @line.length
11
+ @eos = @len == 0 ? true : false
12
+ @i = 0
13
+ end
14
+
15
+ def next
16
+ return nil if @eos
17
+ char = @line[@i]
18
+ @i += 1
19
+ @eos = true if @i > @len
20
+ char
21
+ end
22
+
23
+ def last?
24
+ @i > @len - 1
25
+ end
26
+
27
+ def eos?
28
+ @eos = true if last? # duh?
29
+ @eos
30
+ end
31
+
32
+ def peek
33
+ return nil if @eos
34
+ @line[@i]
35
+ end
36
+
37
+ def skip_spaces
38
+ loop do
39
+ break if peek != " "
40
+ break if eos?
41
+ self.next
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ =begin
48
+ skip
49
+ next! skip! peek!(?)
50
+ expect_alpha
51
+ expect_number
52
+ skip_spaces
53
+ expect_eos
54
+ =end
55
+
data/lib/parser.rb ADDED
@@ -0,0 +1,5 @@
1
+
2
+ require_relative 'parser/string'
3
+ require_relative 'parser/general'
4
+ require_relative 'parser/set'
5
+ require_relative 'parser/mixin'