livetext 0.9.14 → 0.9.15
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/lib/cmdargs.rb +93 -0
- data/lib/formatline.rb +56 -83
- data/lib/helpers.rb +142 -4
- data/lib/livetext.rb +11 -141
- data/lib/parser/file.rb +8 -0
- data/lib/parser/mixin.rb +28 -15
- data/lib/parser/set.rb +35 -26
- data/lib/parser/string.rb +19 -4
- data/lib/processor.rb +1 -4
- data/lib/standard.rb +56 -96
- data/plugin/bookish.rb +26 -22
- data/plugin/calibre.rb +1 -1
- data/plugin/livemagick.rb +10 -10
- data/plugin/markdown.rb +13 -11
- data/plugin/pyggish.rb +94 -84
- data/plugin/tutorial.rb +10 -5
- data/test/all.rb +0 -1
- data/test/snapshots/OMIT.txt +7 -8
- data/test/snapshots/clusion.txt +35 -0
- data/test/snapshots/error_inc_line_num/actual-error.txt +14 -0
- data/test/snapshots/error_inc_line_num/actual-output.txt +7 -0
- data/test/snapshots/error_inc_line_num/match-error.txt +1 -1
- data/test/snapshots/error_inc_line_num/out-sdiff.txt +14 -0
- data/test/snapshots/error_invalid_name/actual-error.txt +10 -0
- data/test/snapshots/error_invalid_name/actual-output.txt +0 -0
- data/test/snapshots/error_invalid_name/match-error.txt +1 -1
- data/test/snapshots/error_invalid_name/out-sdiff.txt +6 -0
- data/test/snapshots/error_line_num/match-error.txt +1 -1
- data/test/snapshots/error_mismatched_end/match-error.txt +1 -1
- data/test/snapshots/error_missing_end/actual-error.txt +10 -0
- data/test/snapshots/error_missing_end/actual-output.txt +0 -0
- data/test/snapshots/error_missing_end/match-error.txt +1 -1
- data/test/snapshots/error_missing_end/out-sdiff.txt +6 -0
- data/test/snapshots/error_no_such_copy/actual-error.txt +10 -0
- data/test/snapshots/error_no_such_copy/actual-output.txt +0 -0
- data/test/snapshots/error_no_such_copy/match-error.txt +1 -1
- data/test/snapshots/error_no_such_copy/out-sdiff.txt +5 -0
- data/test/snapshots/error_no_such_copy/source.lt3 +0 -1
- data/test/snapshots/error_no_such_inc/actual-error.txt +10 -0
- data/test/snapshots/error_no_such_inc/actual-output.txt +0 -0
- data/test/snapshots/error_no_such_inc/match-error.txt +1 -1
- data/test/snapshots/error_no_such_inc/out-sdiff.txt +6 -0
- data/test/snapshots/error_no_such_mixin/actual-error.txt +37 -0
- data/test/snapshots/error_no_such_mixin/actual-output.txt +0 -0
- data/test/snapshots/error_no_such_mixin/out-sdiff.txt +6 -0
- data/test/snapshots/simple_import/actual-error.txt +8 -0
- data/test/snapshots/simple_import/actual-output.txt +3 -0
- data/test/snapshots/simple_import/err-sdiff.txt +9 -0
- data/test/snapshots/simple_import/expected-error.txt +0 -0
- data/test/snapshots/simple_import/expected-output.txt +7 -0
- data/test/snapshots/simple_import/out-sdiff.txt +9 -0
- data/test/snapshots/simple_import/simple_import.rb +5 -0
- data/test/snapshots/simple_import/source.lt3 +7 -0
- data/test/snapshots/simple_include/source.lt3 +0 -1
- data/test/snapshots.rb +3 -2
- data/test/unit/all.rb +1 -0
- data/test/unit/formatline.rb +650 -0
- data/test/unit/parser/importable.rb +1 -1
- data/test/unit/parser/mixin.rb +1 -1
- data/test/unit/parser/set.rb +19 -12
- data/test/unit/parser/string.rb +14 -14
- metadata +32 -5
- data/test/formatting-tests.rb +0 -35
- data/test/formatting.rb +0 -103
- data/test/snapshots/formatting-tests.txt +0 -124
data/lib/livetext.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Class Livetext skeleton (top level).
|
2
2
|
|
3
3
|
class Livetext
|
4
|
-
VERSION = "0.9.
|
4
|
+
VERSION = "0.9.15"
|
5
5
|
Path = File.expand_path(File.join(File.dirname(__FILE__)))
|
6
6
|
end
|
7
7
|
|
@@ -9,13 +9,13 @@ end
|
|
9
9
|
|
10
10
|
require 'fileutils'
|
11
11
|
|
12
|
-
require 'helpers'
|
13
12
|
require_relative 'errors'
|
14
13
|
require_relative 'functions'
|
15
14
|
require_relative 'userapi'
|
16
15
|
require_relative 'standard'
|
17
16
|
require_relative 'formatline'
|
18
17
|
require_relative 'processor'
|
18
|
+
require_relative 'helpers'
|
19
19
|
|
20
20
|
Plugins = File.expand_path(File.join(File.dirname(__FILE__), "../plugin"))
|
21
21
|
|
@@ -31,16 +31,6 @@ class Livetext
|
|
31
31
|
|
32
32
|
Vars = {}
|
33
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
34
|
|
45
35
|
attr_reader :main
|
46
36
|
attr_accessor :no_puts
|
@@ -60,16 +50,17 @@ class Livetext
|
|
60
50
|
call = Array(call)
|
61
51
|
mix.each {|lib| obj.mixin(lib) }
|
62
52
|
call.each {|cmd| obj.main.send(cmd[1..-1]) } # ignores leading dot, no param
|
63
|
-
vars.each_pair {|var, val| obj.
|
53
|
+
vars.each_pair {|var, val| obj.setvar(var, val.to_s) }
|
64
54
|
obj
|
65
55
|
end
|
66
56
|
|
67
57
|
def customize(mix: [], call: [], vars: {})
|
68
58
|
mix = Array(mix)
|
69
59
|
call = Array(call)
|
60
|
+
# FIXME HF won't this break??
|
70
61
|
mix.each {|lib| mixin(lib) }
|
71
62
|
call.each {|cmd| @main.send(cmd[1..-1]) } # ignores leading dot, no param
|
72
|
-
vars.each_pair {|var, val|
|
63
|
+
vars.each_pair {|var, val| setvar(var, val.to_s) }
|
73
64
|
self
|
74
65
|
end
|
75
66
|
|
@@ -82,81 +73,17 @@ class Livetext
|
|
82
73
|
@main = Processor.new(self, output)
|
83
74
|
@indentation = [0]
|
84
75
|
@_vars = Livetext::Vars
|
85
|
-
|
86
|
-
# Other predefined variables (see also _setfile)
|
87
|
-
_setvar(:User, `whoami`.chomp)
|
88
|
-
_setvar(:Version, Livetext::VERSION)
|
89
|
-
end
|
90
|
-
|
91
|
-
def _parse_colon_args(args, hash) # really belongs in livetext
|
92
|
-
h2 = hash.dup
|
93
|
-
e = args.each
|
94
|
-
loop do
|
95
|
-
arg = e.next.chop.to_sym
|
96
|
-
raise "_parse_args: #{arg} is unknown" unless hash.keys.include?(arg)
|
97
|
-
h2[arg] = e.next
|
98
|
-
end
|
99
|
-
h2 = h2.reject {|k,v| v.nil? }
|
100
|
-
h2.each_pair {|k, v| raise "#{k} has no value" if v.empty? }
|
101
|
-
h2
|
102
|
-
end
|
103
|
-
|
104
|
-
def _get_arg(name, args) # really belongs in livetext
|
105
|
-
raise "(#{name}) Expected an array" unless args.is_a? Array
|
106
|
-
raise "(#{name}) Expected an arg" if args.empty?
|
107
|
-
raise "(#{name}) Too many args: #{args.inspect}" if args.size > 1
|
108
|
-
val = args[0]
|
109
|
-
raise "Expected an argument '#{name}'" if val.nil?
|
110
|
-
val
|
111
|
-
end
|
112
|
-
|
113
|
-
def mixin(mod)
|
114
|
-
@main._mixin(mod)
|
115
|
-
end
|
116
|
-
|
117
|
-
def _setvar(var, val)
|
118
|
-
str, sym = var.to_s, var.to_sym
|
119
|
-
Livetext::Vars[str] = val
|
120
|
-
Livetext::Vars[sym] = val
|
121
|
-
@_vars[str] = val
|
122
|
-
@_vars[sym] = val
|
76
|
+
initial_vars
|
123
77
|
end
|
124
78
|
|
125
|
-
def
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
_setvar(:FileDir, dir)
|
130
|
-
else
|
131
|
-
_setvar(:File, "[no file]")
|
132
|
-
_setvar(:FileDir, "[no dir]")
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
def _setfile!(file)
|
137
|
-
_setvar(:File, file)
|
138
|
-
end
|
139
|
-
|
140
|
-
def process_line(line) # FIXME inefficient?
|
141
|
-
nomarkup = true
|
142
|
-
case line # must apply these in order
|
143
|
-
when Comment
|
144
|
-
handle_scomment(line)
|
145
|
-
when Dotcmd
|
146
|
-
handle_dotcmd(line)
|
147
|
-
when Ddotcmd
|
148
|
-
indent = line.index("$") + 1
|
149
|
-
@indentation.push(indent)
|
150
|
-
line.sub!(/^ *\$/, "")
|
151
|
-
handle_dotcmd(line)
|
152
|
-
indentation.pop
|
153
|
-
else
|
154
|
-
@main._passthru(line)
|
155
|
-
end
|
79
|
+
def initial_vars
|
80
|
+
# Other predefined variables (see also setfile)
|
81
|
+
setvar(:User, `whoami`.chomp)
|
82
|
+
setvar(:Version, Livetext::VERSION)
|
156
83
|
end
|
157
84
|
|
158
85
|
def transform(text)
|
159
|
-
|
86
|
+
setfile!("(string)")
|
160
87
|
enum = text.each_line
|
161
88
|
front = text.match(/.*?\n/).to_a.first.chomp rescue ""
|
162
89
|
@main.source(enum, "STDIN: '#{front}...'", 0)
|
@@ -193,62 +120,5 @@ class Livetext
|
|
193
120
|
self.body
|
194
121
|
end
|
195
122
|
|
196
|
-
## FIXME process_file[!] should call process[_text]
|
197
|
-
|
198
|
-
def process_file(fname, btrace=false)
|
199
|
-
_setfile(fname)
|
200
|
-
text = File.readlines(fname)
|
201
|
-
enum = text.each
|
202
|
-
@backtrace = btrace
|
203
|
-
@main.source(enum, fname, 0)
|
204
|
-
line = nil
|
205
|
-
loop do
|
206
|
-
line = @main.nextline
|
207
|
-
break if line.nil?
|
208
|
-
process_line(line)
|
209
|
-
end
|
210
|
-
val = @main.finalize if @main.respond_to? :finalize
|
211
|
-
@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 = ""
|
217
|
-
end
|
218
|
-
|
219
|
-
def handle_scomment(line)
|
220
|
-
end
|
221
|
-
|
222
|
-
def _get_name(line)
|
223
|
-
name, data = line.split(" ", 2)
|
224
|
-
name = name[1..-1] # chop off sigil
|
225
|
-
name = "_" + name if %w[include def].include?(name)
|
226
|
-
@main.data = data
|
227
|
-
@main.check_disallowed(name)
|
228
|
-
name
|
229
|
-
end
|
230
|
-
|
231
|
-
def handle_dotcmd(line, indent = 0)
|
232
|
-
indent = @indentation.last # top of stack
|
233
|
-
line = line.sub(/# .*$/, "")
|
234
|
-
name = _get_name(line).to_sym
|
235
|
-
result = nil
|
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)
|
242
|
-
else
|
243
|
-
@main._error! "Name '#{name}' is unknown"
|
244
|
-
return
|
245
|
-
end
|
246
|
-
result
|
247
|
-
rescue => err
|
248
|
-
puts @body # earlier correct output, not flushed yet
|
249
|
-
STDERR.puts "Error: #{err.inspect}"
|
250
|
-
STDERR.puts err.backtrace
|
251
|
-
end
|
252
|
-
|
253
123
|
end
|
254
124
|
|
data/lib/parser/file.rb
ADDED
data/lib/parser/mixin.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require_relative '../livetext'
|
2
|
+
require_relative '../helpers'
|
2
3
|
require_relative 'string'
|
3
4
|
|
4
5
|
make_exception(:BadVariableName, "Error: invalid variable name")
|
@@ -10,30 +11,42 @@ class Livetext::ParseMixin # < StringParser
|
|
10
11
|
|
11
12
|
include Helpers
|
12
13
|
|
14
|
+
def initialize(name)
|
15
|
+
@name = name
|
16
|
+
@file = find_file(name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.get_module(name)
|
20
|
+
parse = self.new(name)
|
21
|
+
modname, code = parse.read_mixin
|
22
|
+
eval(code) # Avoid in the future
|
23
|
+
newmod = Object.const_get("::" + modname)
|
24
|
+
# return actual module
|
25
|
+
newmod
|
26
|
+
end
|
27
|
+
|
28
|
+
def read_mixin
|
29
|
+
modname = @name.gsub("/","_").capitalize
|
30
|
+
meths = grab_file(@file)
|
31
|
+
[modname, "module ::#{modname}; #{meths}\nend"]
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
13
36
|
def cwd_root?
|
14
37
|
File.dirname(File.expand_path(".")) == "/"
|
15
38
|
end
|
16
39
|
|
17
|
-
def
|
18
|
-
|
40
|
+
def find_file(name, ext=".rb")
|
41
|
+
base = "./#{name}#{ext}"
|
42
|
+
file = "#{Plugins}/#{base}"
|
19
43
|
return file if File.exist?(file)
|
20
44
|
|
21
|
-
file =
|
45
|
+
file = base
|
22
46
|
return file if File.exist?(file)
|
23
47
|
|
24
48
|
raise "No such mixin '#{name}'" if cwd_root?
|
25
|
-
Dir.chdir("..") {
|
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
|
49
|
+
Dir.chdir("..") { find_file(name) }
|
37
50
|
end
|
38
51
|
|
39
52
|
end
|
data/lib/parser/set.rb
CHANGED
@@ -17,22 +17,31 @@ class Livetext::ParseSet < StringParser
|
|
17
17
|
super
|
18
18
|
end
|
19
19
|
|
20
|
+
def wtf(note="")
|
21
|
+
TTY.puts "| PARSER: @i = #@i @len = #@len"
|
22
|
+
TTY.puts "|#{note}"
|
23
|
+
TTY.puts "| [" + @line.gsub(" ", "_") + "]"
|
24
|
+
TTY.print "| " # 0-based (one extra space)
|
25
|
+
@i.times { TTY.print "-" }
|
26
|
+
TTY.puts "^"
|
27
|
+
TTY.puts
|
28
|
+
end
|
29
|
+
|
20
30
|
def parse
|
21
31
|
pairs = []
|
22
|
-
|
32
|
+
char = nil
|
23
33
|
loop do
|
24
|
-
skip_spaces
|
25
|
-
char = self.peek
|
26
34
|
break if eos? # end of string
|
35
|
+
char = skip_spaces
|
27
36
|
raise "Expected alpha to start var name" unless char =~ /[a-z]/i
|
28
37
|
pairs << assignment
|
29
|
-
skip_spaces
|
30
|
-
char = self.peek
|
38
|
+
char = skip_spaces
|
31
39
|
break if eos? # end of string
|
32
40
|
case char
|
33
41
|
when nil # end of string
|
34
42
|
when ","
|
35
|
-
|
43
|
+
char = grab # skip comma
|
44
|
+
char = skip_spaces
|
36
45
|
else
|
37
46
|
raise "Expected comma or end of string (found #{char.inspect})"
|
38
47
|
end
|
@@ -47,7 +56,7 @@ class Livetext::ParseSet < StringParser
|
|
47
56
|
var = get_var
|
48
57
|
skip_equal
|
49
58
|
value = get_value
|
50
|
-
value = FormatLine.var_func_parse(value)
|
59
|
+
value = FormatLine.var_func_parse(value) # FIXME broken now?
|
51
60
|
pair = [var, value]
|
52
61
|
pair
|
53
62
|
end
|
@@ -55,11 +64,11 @@ class Livetext::ParseSet < StringParser
|
|
55
64
|
def get_var
|
56
65
|
name = ""
|
57
66
|
loop do
|
58
|
-
char =
|
67
|
+
char = peek
|
59
68
|
break if eos? # end of string
|
60
69
|
case char
|
61
70
|
when /[a-zA-Z_\.0-9]/
|
62
|
-
name <<
|
71
|
+
name << grab
|
63
72
|
next
|
64
73
|
when /[ =]/
|
65
74
|
return name
|
@@ -73,11 +82,10 @@ class Livetext::ParseSet < StringParser
|
|
73
82
|
def skip_equal
|
74
83
|
found = false
|
75
84
|
skip_spaces
|
76
|
-
raise NoEqualSign unless
|
85
|
+
raise NoEqualSign unless peek == "="
|
77
86
|
found = true
|
78
|
-
|
79
|
-
|
80
|
-
peek = self.peek
|
87
|
+
grab # skip =
|
88
|
+
skip_spaces # skip spaces too
|
81
89
|
return peek # just for testing
|
82
90
|
rescue StopIteration
|
83
91
|
raise NoEqualSign unless found
|
@@ -85,40 +93,41 @@ class Livetext::ParseSet < StringParser
|
|
85
93
|
end
|
86
94
|
|
87
95
|
def escaped
|
88
|
-
|
89
|
-
|
96
|
+
grab # skip backslash
|
97
|
+
grab # return following char
|
90
98
|
end
|
91
99
|
|
92
100
|
make_exception(:BadQuotedString, "Bad quoted string: %1")
|
93
101
|
|
94
102
|
def quoted_value
|
95
|
-
quote =
|
103
|
+
quote = grab # opening quote...
|
96
104
|
value = ""
|
97
105
|
char = nil
|
98
106
|
loop do
|
99
|
-
char =
|
100
|
-
break if
|
107
|
+
char = grab
|
108
|
+
break if eos?
|
101
109
|
break if char == quote
|
110
|
+
# break if char.nil?
|
102
111
|
char = escaped if char == "\\"
|
103
112
|
value << char
|
104
|
-
char = self.next
|
105
113
|
end
|
106
114
|
if char == quote
|
107
|
-
char =
|
115
|
+
# char = grab
|
108
116
|
return value
|
109
117
|
end
|
110
118
|
raise BadQuotedString, quote + value
|
111
119
|
end
|
112
120
|
|
113
121
|
def unquoted_value
|
114
|
-
|
122
|
+
char = nil
|
115
123
|
value = ""
|
116
124
|
loop do
|
117
|
-
char =
|
118
|
-
break if
|
125
|
+
char = peek
|
126
|
+
break if eos? # FIXME oops???
|
127
|
+
# break if char.nil?
|
119
128
|
break if char == " " || char == ","
|
120
129
|
value << char
|
121
|
-
char =
|
130
|
+
char = grab
|
122
131
|
end
|
123
132
|
value
|
124
133
|
end
|
@@ -128,8 +137,8 @@ class Livetext::ParseSet < StringParser
|
|
128
137
|
end
|
129
138
|
|
130
139
|
def get_value
|
131
|
-
char =
|
132
|
-
value = quote?(char) ?
|
140
|
+
char = peek
|
141
|
+
value = quote?(char) ? quoted_value : unquoted_value
|
133
142
|
value
|
134
143
|
end
|
135
144
|
end
|
data/lib/parser/string.rb
CHANGED
@@ -5,14 +5,13 @@ class StringParser
|
|
5
5
|
def initialize(line)
|
6
6
|
raise NilValue if line.nil?
|
7
7
|
raise ExpectedString unless String === line
|
8
|
-
# raise NullString if line.empty?
|
9
8
|
@line = line
|
10
9
|
@len = @line.length
|
11
10
|
@eos = @len == 0 ? true : false
|
12
11
|
@i = 0
|
13
12
|
end
|
14
13
|
|
15
|
-
def
|
14
|
+
def grab
|
16
15
|
return nil if @eos
|
17
16
|
char = @line[@i]
|
18
17
|
@i += 1
|
@@ -20,6 +19,19 @@ class StringParser
|
|
20
19
|
char
|
21
20
|
end
|
22
21
|
|
22
|
+
def ungrab
|
23
|
+
@i -= 1 # FIXME what about eos...?
|
24
|
+
end
|
25
|
+
|
26
|
+
def next!
|
27
|
+
@line[@i + 1]
|
28
|
+
end
|
29
|
+
|
30
|
+
def prev
|
31
|
+
return nil if @i <= 0
|
32
|
+
@line[@i-1]
|
33
|
+
end
|
34
|
+
|
23
35
|
def last?
|
24
36
|
@i > @len - 1
|
25
37
|
end
|
@@ -35,11 +47,14 @@ class StringParser
|
|
35
47
|
end
|
36
48
|
|
37
49
|
def skip_spaces
|
50
|
+
char = nil
|
38
51
|
loop do
|
39
|
-
|
52
|
+
char = peek
|
40
53
|
break if eos?
|
41
|
-
|
54
|
+
break if char != " "
|
55
|
+
grab
|
42
56
|
end
|
57
|
+
char
|
43
58
|
end
|
44
59
|
|
45
60
|
end
|
data/lib/processor.rb
CHANGED
@@ -45,7 +45,7 @@ class Livetext
|
|
45
45
|
raise GenericError.new("Error: #{err}") if raise_error
|
46
46
|
end
|
47
47
|
|
48
|
-
def
|
48
|
+
def disallowed?(name)
|
49
49
|
Disallowed.include?(name.to_sym)
|
50
50
|
end
|
51
51
|
|
@@ -71,8 +71,5 @@ class Livetext
|
|
71
71
|
@sources.pop
|
72
72
|
nil
|
73
73
|
end
|
74
|
-
|
75
|
-
|
76
74
|
end
|
77
|
-
|
78
75
|
end
|