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.
- checksums.yaml +4 -4
- data/bin/livetext +0 -1
- data/lib/errors.rb +15 -0
- data/lib/formatline.rb +3 -5
- data/lib/functions.rb +6 -2
- data/lib/helpers.rb +25 -0
- data/lib/html.rb +32 -0
- data/lib/livetext/importable.rb +2 -0
- data/lib/livetext.rb +60 -48
- data/lib/parser/general.rb +38 -0
- data/lib/parser/import.rb +17 -0
- data/lib/parser/mixin.rb +40 -0
- data/lib/parser/set.rb +136 -0
- data/lib/parser/string.rb +55 -0
- data/lib/parser.rb +5 -0
- data/lib/processor.rb +22 -23
- data/lib/standard.rb +145 -298
- data/lib/userapi.rb +2 -5
- data/livetext.gemspec +1 -2
- data/test/all.rb +4 -0
- data/test/formatting-tests.rb +35 -0
- data/test/formatting.rb +2 -9
- data/test/snapshots/OMIT.txt +10 -0
- data/test/{data → snapshots}/basic_formatting/expected-error.txt +0 -0
- data/test/{data → snapshots}/basic_formatting/expected-output.txt +0 -0
- data/test/{data → snapshots}/basic_formatting/source.lt3 +0 -0
- data/test/{data → snapshots}/block_comment/expected-error.txt +0 -0
- data/test/{data → snapshots}/block_comment/expected-output.txt +0 -0
- data/test/{data → snapshots}/block_comment/source.lt3 +0 -0
- data/test/{data → snapshots}/comments_ignored_1/expected-error.txt +0 -0
- data/test/{data → snapshots}/comments_ignored_1/expected-output.txt +0 -0
- data/test/{data → snapshots}/comments_ignored_1/source.lt3 +0 -0
- data/test/{data → snapshots}/copy_is_raw/expected-error.txt +0 -0
- data/test/{data → snapshots}/copy_is_raw/expected-output.txt +0 -0
- data/test/{data → snapshots}/copy_is_raw/rawtext.inc +0 -0
- data/test/{data → snapshots}/copy_is_raw/source.lt3 +0 -0
- data/test/{data → snapshots}/crap +0 -0
- data/test/{data → snapshots}/def_method/expected-error.txt +0 -0
- data/test/{data → snapshots}/def_method/expected-output.txt +0 -0
- data/test/{data → snapshots}/def_method/source.lt3 +0 -0
- data/test/{data → snapshots}/error_inc_line_num/expected-output.txt +6 -0
- data/test/{data → snapshots}/error_inc_line_num/file2.lt3 +0 -0
- data/test/snapshots/error_inc_line_num/match-error.txt +1 -0
- data/test/{data → snapshots}/error_inc_line_num/source.lt3 +0 -0
- data/test/{data → snapshots}/error_invalid_name/expected-output.txt +0 -0
- data/test/snapshots/error_invalid_name/match-error.txt +1 -0
- data/test/{data → snapshots}/error_invalid_name/source.lt3 +0 -0
- data/test/{data → snapshots}/error_line_num/expected-output.txt +0 -0
- data/test/snapshots/error_line_num/match-error.txt +1 -0
- data/test/{data → snapshots}/error_line_num/source.lt3 +0 -0
- data/test/{data → snapshots}/error_mismatched_end/expected-output.txt +0 -0
- data/test/snapshots/error_mismatched_end/match-error.txt +1 -0
- data/test/{data → snapshots}/error_mismatched_end/source.lt3 +0 -0
- data/test/{data → snapshots}/error_missing_end/expected-output.txt +1 -0
- data/test/snapshots/error_missing_end/match-error.txt +1 -0
- data/test/{data → snapshots}/error_missing_end/source.lt3 +0 -0
- data/test/{data/error_no_such_mixin → snapshots/error_name_not_permitted}/expected-output.txt +0 -0
- data/test/snapshots/error_name_not_permitted/match-error.txt +1 -0
- data/test/{data → snapshots}/error_name_not_permitted/source.lt3 +0 -0
- data/test/{data → snapshots}/error_no_such_copy/expected-output.txt +0 -1
- data/test/snapshots/error_no_such_copy/match-error.txt +1 -0
- data/test/{data → snapshots}/error_no_such_copy/source.lt3 +1 -0
- data/test/{data → snapshots}/error_no_such_inc/expected-output.txt +0 -0
- data/test/snapshots/error_no_such_inc/match-error.txt +1 -0
- data/test/{data → snapshots}/error_no_such_inc/source.lt3 +0 -0
- data/test/snapshots/error_no_such_mixin/expected-output.txt +5 -0
- data/test/snapshots/error_no_such_mixin/match-error.txt +1 -0
- data/test/{data → snapshots}/error_no_such_mixin/source.lt3 +0 -0
- data/test/{data → snapshots}/example_alpha/expected-error.txt +0 -0
- data/test/{data → snapshots}/example_alpha/expected-output.txt +0 -0
- data/test/{data → snapshots}/example_alpha/source.lt3 +0 -0
- data/test/{data → snapshots}/example_alpha2/expected-error.txt +0 -0
- data/test/{data → snapshots}/example_alpha2/expected-output.txt +0 -0
- data/test/{data → snapshots}/example_alpha2/source.lt3 +0 -0
- data/test/{data → snapshots}/fixit +0 -0
- data/test/{data/lines.txt → snapshots/formatting-tests.txt} +4 -0
- data/test/{data → snapshots}/functions/expected-error.txt +0 -0
- data/test/{data → snapshots}/functions/expected-output.txt +0 -0
- data/test/{data → snapshots}/functions/source.lt3 +0 -0
- data/test/{data → snapshots}/hello_world/expected-error.txt +0 -0
- data/test/{data → snapshots}/hello_world/expected-output.txt +0 -0
- data/test/{data → snapshots}/hello_world/source.lt3 +0 -0
- data/test/{data → snapshots}/more_complex_vars/expected-error.txt +0 -0
- data/test/{data → snapshots}/more_complex_vars/expected-output.txt +0 -0
- data/test/{data → snapshots}/more_complex_vars/source.lt3 +0 -0
- data/test/{data/raw_lines → snapshots/predef_vars}/expected-error.txt +0 -0
- data/test/snapshots/predef_vars/match-output.txt +6 -0
- data/test/snapshots/predef_vars/source.lt3 +6 -0
- data/test/{data/raw_text_block → snapshots/raw_lines}/expected-error.txt +0 -0
- data/test/{data → snapshots}/raw_lines/expected-output.txt +0 -0
- data/test/{data → snapshots}/raw_lines/source.lt3 +0 -0
- data/test/{data/simple_copy → snapshots/raw_text_block}/expected-error.txt +0 -0
- data/test/{data → snapshots}/raw_text_block/expected-output.txt +0 -0
- data/test/{data → snapshots}/raw_text_block/rawtext.inc +0 -0
- data/test/{data → snapshots}/raw_text_block/source.lt3 +0 -0
- data/test/{data/simple_include → snapshots/simple_copy}/expected-error.txt +0 -0
- data/test/{data → snapshots}/simple_copy/expected-output.txt +0 -0
- data/test/{data → snapshots}/simple_copy/simplefile.inc +0 -0
- data/test/{data → snapshots}/simple_copy/source.lt3 +0 -0
- data/test/{data/simple_mixin → snapshots/simple_include}/expected-error.txt +0 -0
- data/test/{data → snapshots}/simple_include/expected-output.txt +0 -0
- data/test/{data → snapshots}/simple_include/simplefile.inc +0 -0
- data/test/{data → snapshots}/simple_include/source.lt3 +0 -0
- data/test/{data/simple_vars → snapshots/simple_mixin}/expected-error.txt +0 -0
- data/test/{data → snapshots}/simple_mixin/expected-output.txt +0 -0
- data/test/{data → snapshots}/simple_mixin/simple_mixin.rb +0 -0
- data/test/{data → snapshots}/simple_mixin/source.lt3 +0 -0
- data/test/{data/single_raw_line → snapshots/simple_vars}/expected-error.txt +0 -0
- data/test/{data → snapshots}/simple_vars/expected-output.txt +0 -0
- data/test/{data → snapshots}/simple_vars/source.lt3 +0 -0
- data/test/{data/table_with_heredocs → snapshots/single_raw_line}/expected-error.txt +0 -0
- data/test/{data → snapshots}/single_raw_line/expected-output.txt +0 -0
- data/test/{data → snapshots}/single_raw_line/source.lt3 +0 -0
- data/test/{data → snapshots}/subset.txt +0 -0
- data/test/snapshots/table_with_heredocs/expected-error.txt +0 -0
- data/test/{data → snapshots}/table_with_heredocs/expected-output.txt +0 -0
- data/test/{data → snapshots}/table_with_heredocs/source.lt3 +0 -0
- data/test/snapshots.rb +168 -0
- data/test/testlines.rb +17 -7
- data/test/unit/all.rb +3 -0
- data/test/unit/html.rb +38 -0
- data/test/unit/parser/all.rb +3 -0
- data/test/unit/parser/general.rb +59 -0
- data/test/unit/parser/importable.rb +19 -0
- data/test/unit/parser/mixin.rb +19 -0
- data/test/unit/parser/set.rb +157 -0
- data/test/unit/parser/string.rb +130 -0
- data/test/unit/parser.rb +6 -0
- data/test/unit/standard.rb +23 -0
- data/test/unit/stringparser.rb +140 -0
- metadata +122 -96
- data/test/data/error_inc_line_num/expected-err-line1match.txt +0 -1
- data/test/data/error_invalid_name/expected-err-line1match.txt +0 -1
- data/test/data/error_line_num/expected-err-line1match.txt +0 -1
- data/test/data/error_mismatched_end/expected-err-line1match.txt +0 -1
- data/test/data/error_missing_end/expected-err-line1match.txt +0 -1
- data/test/data/error_name_not_permitted/expected-error.txt +0 -1
- data/test/data/error_name_not_permitted/expected-output.txt +0 -4
- data/test/data/error_no_such_copy/expected-err-line1match.txt +0 -1
- data/test/data/error_no_such_inc/expected-err-line1match.txt +0 -1
- data/test/data/error_no_such_mixin/expected-err-line1match.txt +0 -1
- data/test/extratests.txt +0 -20
- data/test/test.rb +0 -140
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d9e6ad9c8d933b361da03fadc6cc4d28e0a8b699f8f50e434db9bf41f0b9d66b
|
4
|
+
data.tar.gz: 49e59704162aea72ac92122d69f0006591856b9fe09f2b2fdbf6843b7253b80b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74b1b06e0bc91d976c0f60a2136ed8bb1c4d2d5ba33359620752b44a51918a89fbba2cec9c542ae12a6368de82bdc4db64b30a4d95146caab4e5a290f9c7fa8b
|
7
|
+
data.tar.gz: ed0c0fa3b4da538ddbd31cd799a84f5647d45a369f0f45d729c223f74c7b0f2445bfc0812aa0c319b9a9f5992efa6af521d550eed38a8e2c551b183e28a16722
|
data/bin/livetext
CHANGED
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
|
-
|
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
|
data/lib/livetext.rb
CHANGED
@@ -1,34 +1,53 @@
|
|
1
|
+
# Class Livetext skeleton (top level).
|
2
|
+
|
1
3
|
class Livetext
|
2
|
-
VERSION = "0.9.
|
4
|
+
VERSION = "0.9.14"
|
3
5
|
Path = File.expand_path(File.join(File.dirname(__FILE__)))
|
4
6
|
end
|
5
7
|
|
6
|
-
|
8
|
+
# $LOAD_PATH << Livetext::Path
|
7
9
|
|
8
|
-
|
10
|
+
require 'fileutils'
|
9
11
|
|
10
|
-
require '
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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 :
|
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
|
-
|
115
|
-
|
116
|
-
|
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
|
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
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
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
|
-
|
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
|
-
|
226
|
-
|
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
|
-
|
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
|
+
|
data/lib/parser/mixin.rb
ADDED
@@ -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
|
+
|