livetext 0.9.23 → 0.9.26
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.lt3 +6 -6
- data/bin/livetext +57 -40
- data/imports/bookish.rb +81 -81
- data/imports/calibre.rb +3 -3
- data/imports/livemagick.rb +17 -17
- data/imports/markdown.rb +10 -10
- data/imports/pyggish.rb +13 -13
- data/imports/tutorial.rb +15 -15
- data/lib/cmdargs.rb +7 -4
- data/lib/{errors.rb → livetext/errors.rb} +4 -3
- data/lib/{formatline.rb → livetext/formatline.rb} +119 -18
- data/lib/livetext/funcall.rb +168 -0
- data/lib/{functions.rb → livetext/functions.rb} +0 -2
- data/lib/{global_helpers.rb → livetext/global_helpers.rb} +6 -3
- data/lib/{handler → livetext/handler}/import.rb +5 -9
- data/lib/livetext/handler/mixin.rb +33 -0
- data/lib/{handler.rb → livetext/handler.rb} +1 -1
- data/lib/{helpers.rb → livetext/helpers.rb} +78 -66
- data/lib/{html.rb → livetext/html.rb} +2 -3
- data/lib/livetext/lineparser.rb +441 -0
- data/lib/livetext/more.rb +158 -0
- data/lib/{parser → livetext/parser}/general.rb +0 -0
- data/lib/{parser → livetext/parser}/set.rb +0 -0
- data/lib/{parser → livetext/parser}/string.rb +0 -0
- data/lib/{parser.rb → livetext/parser.rb} +0 -3
- data/lib/{parsing.rb → livetext/parsing.rb} +0 -2
- data/lib/livetext/paths.rb +13 -0
- data/lib/{processor.rb → livetext/processor.rb} +18 -8
- data/lib/livetext/reopen.rb +12 -0
- data/lib/livetext/skeleton.rb +22 -0
- data/lib/{standard.rb → livetext/standard.rb} +150 -127
- data/lib/livetext/userapi.rb +170 -0
- data/lib/livetext/version.rb +6 -0
- data/lib/livetext.rb +14 -152
- data/plugin/bookish.rb +82 -81
- data/plugin/calibre.rb +3 -3
- data/plugin/livemagick.rb +17 -17
- data/plugin/markdown.rb +10 -10
- data/plugin/pyggish.rb +118 -118
- data/plugin/tutorial.rb +15 -15
- data/test/all.rb +6 -0
- data/test/snapshots/{error_inc_line_num → basic_formatting}/actual-error.txt +0 -0
- data/test/snapshots/basic_formatting/actual-output.txt +13 -0
- data/test/snapshots/basic_formatting/err-sdiff.txt +1 -0
- data/test/snapshots/basic_formatting/out-sdiff.txt +14 -0
- data/test/snapshots/def_method/expected-output.txt +2 -0
- data/test/snapshots/def_method/source.lt3 +4 -2
- data/test/snapshots/error_inc_line_num/{OUT → README.txt} +11 -8
- data/test/snapshots/error_inc_line_num/expected-output.txt +0 -6
- data/test/snapshots/error_inc_line_num/match-error.txt +1 -1
- data/test/snapshots/error_invalid_name/foo +5 -0
- data/test/snapshots/error_line_num/match-error.txt +1 -1
- data/test/snapshots/error_missing_end/expected-output.txt +0 -1
- data/test/snapshots/error_name_not_permitted/expected-output.txt +4 -0
- data/test/snapshots/error_name_not_permitted/match-error.txt +1 -1
- data/test/snapshots/error_no_such_copy/expected-output.txt +1 -0
- data/test/snapshots/error_no_such_mixin/expected-output.txt +1 -0
- data/test/snapshots/error_no_such_mixin/match-error.txt +1 -1
- data/test/snapshots/error_no_such_mixin/source.lt3 +1 -1
- data/test/snapshots/example_alpha/source.lt3 +2 -2
- data/test/snapshots/example_alpha2/expected-output.txt +0 -2
- data/test/snapshots/example_alpha2/source.lt3 +5 -4
- data/test/snapshots/import/expected-output.txt +2 -1
- data/test/snapshots/import/match-error.txt +1 -1
- data/test/snapshots/import/simple_import.rb +1 -1
- data/test/snapshots/import2/simple_import.rb +1 -1
- data/test/snapshots/import_bookish/expected-output.txt +4 -4
- data/test/snapshots/{error_invalid_name/actual-output.txt → more_functions/actual-error.txt} +0 -0
- data/test/snapshots/more_functions/actual-output.txt +37 -0
- data/test/snapshots/more_functions/err-sdiff.txt +1 -0
- data/test/snapshots/more_functions/expected-output.txt +1 -1
- data/test/snapshots/more_functions/out-sdiff.txt +38 -0
- data/test/snapshots/more_functions/source.lt3 +1 -1
- data/test/snapshots/raw_lines/expected-output.txt +0 -2
- data/test/snapshots/simple_import/simple_import.rb +1 -1
- data/test/snapshots/simple_mixin/simple_mixin.rb +1 -1
- data/test/snapshots/{error_missing_end/actual-output.txt → simple_vars/actual-error.txt} +0 -0
- data/test/snapshots/simple_vars/actual-output.txt +6 -0
- data/test/snapshots/simple_vars/err-sdiff.txt +1 -0
- data/test/snapshots/simple_vars/out-sdiff.txt +7 -0
- data/test/snapshots/single_raw_line/expected-output.txt +0 -2
- data/test/snapshots/subset.txt +9 -7
- data/test/snapshots/{error_no_such_copy/actual-output.txt → var_into_func/actual-error.txt} +0 -0
- data/test/snapshots/var_into_func/actual-output.txt +16 -0
- data/test/snapshots/var_into_func/err-sdiff.txt +1 -0
- data/test/snapshots/{error_no_such_inc/actual-output.txt → var_into_func/expected-error.txt} +0 -0
- data/test/snapshots/var_into_func/expected-output.txt +16 -0
- data/test/snapshots/var_into_func/out-sdiff.txt +17 -0
- data/test/snapshots/var_into_func/source.lt3 +16 -0
- data/test/snapshots.rb +16 -7
- data/test/unit/all.rb +3 -1
- data/test/unit/formatline.rb +145 -276
- data/test/unit/html.rb +1 -2
- data/test/unit/lineparser.rb +650 -0
- data/test/unit/parser/set.rb +13 -12
- data/test/unit/standard.rb +0 -1
- data/test/unit/tokenizer.rb +534 -0
- metadata +49 -39
- data/lib/funcall.rb +0 -93
- data/lib/parser/file.rb +0 -6
- data/lib/parser/mixin.rb +0 -34
- data/lib/userapi.rb +0 -164
- data/test/snapshots/error_inc_line_num/actual-output.txt +0 -17
- data/test/snapshots/error_invalid_name/actual-error.txt +0 -10
- data/test/snapshots/error_invalid_name/out-sdiff.txt +0 -6
- data/test/snapshots/error_missing_end/actual-error.txt +0 -10
- data/test/snapshots/error_missing_end/out-sdiff.txt +0 -6
- data/test/snapshots/error_no_such_copy/actual-error.txt +0 -10
- data/test/snapshots/error_no_such_copy/out-sdiff.txt +0 -5
- data/test/snapshots/error_no_such_inc/actual-error.txt +0 -10
- data/test/snapshots/error_no_such_inc/out-sdiff.txt +0 -6
- data/test/snapshots/error_no_such_mixin/actual-error.txt +0 -13
- data/test/snapshots/error_no_such_mixin/actual-output.txt +0 -0
- data/test/snapshots/error_no_such_mixin/out-sdiff.txt +0 -6
@@ -1,5 +1,5 @@
|
|
1
|
-
# p __FILE__
|
2
1
|
|
2
|
+
require_relative 'global_helpers'
|
3
3
|
|
4
4
|
module Livetext::Helpers
|
5
5
|
|
@@ -9,6 +9,14 @@ module Livetext::Helpers
|
|
9
9
|
ESCAPING = { "'" => ''', '&' => '&', '"' => '"',
|
10
10
|
'<' => '<', '>' => '>' }
|
11
11
|
|
12
|
+
def friendly_error(err)
|
13
|
+
return graceful_error(err) if self.respond_to?(:graceful_error)
|
14
|
+
return self.parent.graceful_error(err) if self.respond_to?(:parent)
|
15
|
+
raise err
|
16
|
+
rescue => myerr
|
17
|
+
TTY.puts "--- Warning: friendly_error #{myerr.inspect}"
|
18
|
+
end
|
19
|
+
|
12
20
|
def escape_html(string)
|
13
21
|
enc = string.encoding
|
14
22
|
unless enc.ascii_compatible?
|
@@ -25,34 +33,47 @@ module Livetext::Helpers
|
|
25
33
|
string.gsub(/['&\"<>]/, ESCAPING)
|
26
34
|
end
|
27
35
|
|
36
|
+
def showme(obj, tag = "")
|
37
|
+
whence = caller[0]
|
38
|
+
file, line, meth = whence.split(":")
|
39
|
+
file = File.basename(file)
|
40
|
+
meth = meth[4..-2]
|
41
|
+
tag << " =" if tag
|
42
|
+
hide_class = [true, false, nil].include?(obj)
|
43
|
+
klass = hide_class ? "" : "(#{obj.class}) "
|
44
|
+
puts " #{tag} #{klass}#{obj.inspect} in ##{meth} [#{file} line #{line}]"
|
45
|
+
end
|
46
|
+
|
47
|
+
def debug(*args)
|
48
|
+
puts(*args) if ENV['debug']
|
49
|
+
end
|
50
|
+
|
28
51
|
def find_file(name, ext=".rb", which="imports")
|
29
52
|
failed = "#{__method__}: expected 'imports' or 'plugin'"
|
30
53
|
raise failed unless %w[imports plugin].include?(which)
|
31
|
-
paths = [Livetext::Path.sub(/lib/, "#{which}/"), "./"]
|
54
|
+
paths = [Livetext::Path.sub(/lib.livetext/, "#{which}/"), "./"]
|
32
55
|
base = "#{name}#{ext}"
|
33
56
|
paths.each do |path|
|
34
57
|
file = path + base
|
58
|
+
::Livetext::TTY.puts " Checking: #{file}"
|
35
59
|
return file if File.exist?(file)
|
36
60
|
end
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
# # Really want to search upward??
|
41
|
-
# raise "No such mixin '#{name}'" if cwd_root?
|
42
|
-
# Dir.chdir("..") { find_file(name) }
|
61
|
+
::Livetext::TTY.puts " ...oops"
|
62
|
+
return nil
|
43
63
|
end
|
44
64
|
|
45
65
|
def self.rx(str, space=nil)
|
46
66
|
Regexp.compile("^" + Regexp.escape(str) + "#{space}")
|
47
67
|
end
|
48
68
|
|
49
|
-
Comment
|
50
|
-
|
51
|
-
|
69
|
+
Comment = rx(Sigil, Space)
|
70
|
+
DotCmd = rx(Sigil)
|
71
|
+
DollarDot = /^ *\$\.[A-Za-z]/
|
52
72
|
|
53
|
-
## FIXME process_file[!] should call process[_text]
|
73
|
+
## FIXME process_file[!] should call process[_text] ?
|
54
74
|
|
55
75
|
def process_file(fname, btrace=false)
|
76
|
+
graceful_error FileNotFound(fname) unless File.exist?(fname)
|
56
77
|
setfile(fname)
|
57
78
|
text = File.readlines(fname)
|
58
79
|
enum = text.each
|
@@ -62,77 +83,64 @@ module Livetext::Helpers
|
|
62
83
|
loop do
|
63
84
|
line = @main.nextline
|
64
85
|
break if line.nil?
|
65
|
-
process_line(line)
|
86
|
+
success = process_line(line)
|
87
|
+
break unless success
|
66
88
|
end
|
67
89
|
val = @main.finalize rescue nil
|
68
90
|
@body # FIXME? @body.join("\n") # array
|
69
|
-
|
70
|
-
# TTY.puts ">>> rescue in process_file!! (helpers)"
|
71
|
-
# TTY.puts @body
|
72
|
-
raise err
|
91
|
+
return true
|
73
92
|
end
|
74
93
|
|
75
94
|
def process_line(line)
|
76
|
-
|
95
|
+
success = true
|
77
96
|
case line # must apply these in order
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
@indentation.push(indent)
|
85
|
-
line.sub!(/^ *\$/, "")
|
86
|
-
handle_dotcmd(line)
|
87
|
-
indentation.pop
|
97
|
+
when Comment
|
98
|
+
success = handle_scomment(line)
|
99
|
+
when DotCmd
|
100
|
+
success = handle_dotcmd(line)
|
101
|
+
when DollarDot
|
102
|
+
success = handle_dollar_dot
|
88
103
|
else
|
89
|
-
|
104
|
+
api.passthru(line) # must succeed?
|
90
105
|
end
|
106
|
+
success
|
107
|
+
end
|
108
|
+
|
109
|
+
def handle_dollar_dot
|
110
|
+
indent = line.index("$") + 1
|
111
|
+
@indentation.push(indent)
|
112
|
+
line.sub!(/^ *\$/, "")
|
113
|
+
success = handle_dotcmd(line)
|
114
|
+
indentation.pop
|
115
|
+
success
|
116
|
+
end
|
117
|
+
|
118
|
+
def invoke_dotcmd(name)
|
119
|
+
# FIXME Add cmdargs stuff... depends on name, etc.
|
120
|
+
retval = @main.send(name)
|
121
|
+
retval
|
122
|
+
rescue => err
|
123
|
+
graceful_error(err)
|
91
124
|
end
|
92
125
|
|
93
126
|
def handle_dotcmd(line, indent = 0)
|
94
127
|
indent = @indentation.last # top of stack
|
95
|
-
line = line.sub(/# .*$/, "")
|
96
|
-
name = get_name(line)
|
97
|
-
|
128
|
+
line = line.sub(/# .*$/, "") # FIXME Could be problematic?
|
129
|
+
name = get_name(line)
|
130
|
+
success = true # Be optimistic... :P
|
98
131
|
case
|
99
132
|
when name == :end # special case
|
100
|
-
|
101
|
-
raise EndWithoutOpening()
|
133
|
+
graceful_error EndWithoutOpening()
|
102
134
|
when @main.respond_to?(name)
|
103
|
-
|
104
|
-
|
105
|
-
# NOTE: The above line is where the magic happens!
|
106
|
-
# A name like 'foobar' results in an invocation of
|
107
|
-
# @main.foobar (where @main is a Processor, and any
|
108
|
-
# new methods (e.g. from a mixin) are added to @main
|
109
|
-
#
|
110
|
-
# So all the functionality from _args and _raw_args
|
111
|
-
# and _data (among others?) will be encapsulated in
|
112
|
-
# 'some' kind of PORO which handles access to all
|
113
|
-
# these things as well as the 'body' between the
|
114
|
-
# command and its corresponding .end
|
115
|
-
#
|
116
|
-
# The 'body' functionality is so commonly used, I plan
|
117
|
-
# to pass it in separately as needed (even though the
|
118
|
-
# args object should make it available also).
|
119
|
-
#
|
120
|
-
# Every method corresponding to a dot commmand will
|
121
|
-
# get args and body passed in as needed. Every one of
|
122
|
-
# the signatures already has (args = nil, body = nil)
|
123
|
-
# but nothing is being passed in that way yet.
|
124
|
-
#
|
125
|
-
# Refer to lib/cmdargs.rb for more! This is *strictly*
|
126
|
-
# experimental and a "work in progress."
|
135
|
+
success = invoke_dotcmd(name)
|
127
136
|
else
|
128
|
-
|
129
|
-
raise "Name '#{name}' is unknown"
|
130
|
-
return
|
137
|
+
graceful_error UnknownMethod(name)
|
131
138
|
end
|
132
|
-
|
139
|
+
success
|
133
140
|
end
|
134
141
|
|
135
142
|
def handle_scomment(line)
|
143
|
+
return true
|
136
144
|
end
|
137
145
|
|
138
146
|
def get_name(line)
|
@@ -141,15 +149,16 @@ module Livetext::Helpers
|
|
141
149
|
name = "dot_" + name if %w[include def].include?(name)
|
142
150
|
@main.check_disallowed(name)
|
143
151
|
@main.data = data
|
144
|
-
|
152
|
+
@main.api.data = data
|
153
|
+
name.to_sym
|
145
154
|
end
|
146
155
|
|
147
156
|
def check_disallowed(name)
|
148
|
-
|
157
|
+
friendly_error DisallowedName(name) if disallowed?(name)
|
149
158
|
end
|
150
159
|
|
151
160
|
def check_file_exists(file)
|
152
|
-
|
161
|
+
return File.exist?(file)
|
153
162
|
end
|
154
163
|
|
155
164
|
def set_variables(pairs)
|
@@ -161,6 +170,9 @@ module Livetext::Helpers
|
|
161
170
|
|
162
171
|
def grab_file(fname)
|
163
172
|
File.read(fname)
|
173
|
+
rescue
|
174
|
+
::STDERR.puts "Can't find #{fname.inspect} \n "
|
175
|
+
return nil
|
164
176
|
end
|
165
177
|
|
166
178
|
def search_upward(file)
|
@@ -185,7 +197,7 @@ module Livetext::Helpers
|
|
185
197
|
end
|
186
198
|
|
187
199
|
def include_file(file)
|
188
|
-
|
200
|
+
api.args = [file]
|
189
201
|
dot_include
|
190
202
|
end
|
191
203
|
|
@@ -1,4 +1,3 @@
|
|
1
|
-
# p __FILE__
|
2
1
|
|
3
2
|
module HTMLHelper
|
4
3
|
|
@@ -17,9 +16,9 @@ module HTMLHelper
|
|
17
16
|
|
18
17
|
def wrap(*tags) # helper
|
19
18
|
open, close = open_close_tags(*tags)
|
20
|
-
|
19
|
+
api.out open
|
21
20
|
yield
|
22
|
-
|
21
|
+
api.out close
|
23
22
|
end
|
24
23
|
|
25
24
|
def open_close_tags(*tags)
|
@@ -0,0 +1,441 @@
|
|
1
|
+
|
2
|
+
require_relative 'parsing'
|
3
|
+
require_relative 'funcall'
|
4
|
+
|
5
|
+
# Class LineParser handles the parsing of comments, dot commands, and
|
6
|
+
# simple formatting characters, as well as variables and functions.
|
7
|
+
|
8
|
+
class Livetext::LineParser < StringParser
|
9
|
+
include Livetext::ParsingConstants
|
10
|
+
include Livetext::LineParser::FunCall
|
11
|
+
|
12
|
+
FMTS = %w[* _ ~ `]
|
13
|
+
|
14
|
+
attr_reader :out
|
15
|
+
attr_reader :tokenlist
|
16
|
+
|
17
|
+
def initialize(line)
|
18
|
+
super
|
19
|
+
@token = Null.dup
|
20
|
+
@tokenlist = []
|
21
|
+
@live = Livetext.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.api
|
25
|
+
Livetext.new.main.api
|
26
|
+
end
|
27
|
+
|
28
|
+
def api
|
29
|
+
@live.main.api
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.parse!(line)
|
33
|
+
return nil if line.nil?
|
34
|
+
line.chomp!
|
35
|
+
x = self.new(line)
|
36
|
+
api.tty "\n-- string: #{line.inspect}" if $testme
|
37
|
+
t = x.tokenize
|
38
|
+
api.tty "-- Tokens: #{t.inspect}" if $testme
|
39
|
+
result = x.evaluate
|
40
|
+
api.tty "-- result: #{result.inspect}" if $testme
|
41
|
+
result
|
42
|
+
end
|
43
|
+
|
44
|
+
def parse_formatting
|
45
|
+
loop do
|
46
|
+
case peek
|
47
|
+
when Escape; grab; add peek; grab
|
48
|
+
when "*", "_", "`", "~"
|
49
|
+
marker peek
|
50
|
+
add peek
|
51
|
+
when LF
|
52
|
+
break if eos?
|
53
|
+
when nil
|
54
|
+
break
|
55
|
+
else
|
56
|
+
add peek
|
57
|
+
end
|
58
|
+
grab
|
59
|
+
end
|
60
|
+
add_token(:str)
|
61
|
+
@tokenlist
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.parse_formatting(str)
|
65
|
+
fmt = self.new(str)
|
66
|
+
loop do
|
67
|
+
case fmt.peek
|
68
|
+
when Escape; fmt.grab; fmt.add fmt.peek; fmt.grab
|
69
|
+
when "*", "_", "`", "~"
|
70
|
+
fmt.marker fmt.peek
|
71
|
+
fmt.add fmt.peek
|
72
|
+
when LF
|
73
|
+
break if fmt.eos?
|
74
|
+
when nil
|
75
|
+
break
|
76
|
+
else
|
77
|
+
fmt.add fmt.peek
|
78
|
+
end
|
79
|
+
fmt.grab
|
80
|
+
end
|
81
|
+
fmt.add_token(:str)
|
82
|
+
fmt.tokenlist
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.parse_variables(str)
|
86
|
+
return nil if str.nil?
|
87
|
+
x = self.new(str.chomp)
|
88
|
+
char = x.peek
|
89
|
+
loop do
|
90
|
+
char = x.grab
|
91
|
+
break if char == LF || char == nil
|
92
|
+
x.escaped if char == Escape
|
93
|
+
x.dollar if char == "$" # Could be $$
|
94
|
+
x.add char
|
95
|
+
end
|
96
|
+
x.add_token(:str)
|
97
|
+
result = x.evaluate
|
98
|
+
result
|
99
|
+
end
|
100
|
+
|
101
|
+
def embed(sym, str)
|
102
|
+
pre, post = SimpleFormats[sym]
|
103
|
+
pre + str + post
|
104
|
+
end
|
105
|
+
|
106
|
+
#########
|
107
|
+
|
108
|
+
def handle_simple_formatting(ch)
|
109
|
+
# api.tty "ch = #{ch.inspect}"
|
110
|
+
marker ch
|
111
|
+
add ch
|
112
|
+
# api.tty "token = #{@token.inspect} #{@tokenlist.inspect}"
|
113
|
+
c2 = grab
|
114
|
+
# api.tty "c2 = #{c2.inspect}"
|
115
|
+
end
|
116
|
+
|
117
|
+
def grab_string
|
118
|
+
weird = ["$", nil] # [Escape, "$", nil]
|
119
|
+
ch = grab
|
120
|
+
add ch # api.tty "-- gs @token = #{@token.inspect}"
|
121
|
+
loop do
|
122
|
+
ch = peek # api.tty "gs1 ch = #{ch.inspect}"
|
123
|
+
break if weird.include?(ch)
|
124
|
+
break if FMTS.include?(ch) && (self.prev == " ")
|
125
|
+
break if eos? # ch = grab # advance pointer # api.tty "gs3 ch = #{ch.inspect}"
|
126
|
+
add grab
|
127
|
+
end # ch = grab # advance pointer # api.tty "-- gs4 ch = #{ch.inspect}"; sleep 0.01
|
128
|
+
add_token :str
|
129
|
+
end
|
130
|
+
|
131
|
+
def grab_token(ch)
|
132
|
+
finish = false
|
133
|
+
# api.tty "#{__method__}: ch = #{ch.inspect}"
|
134
|
+
case ch
|
135
|
+
when nil; finish = true # do nothing
|
136
|
+
when LF; finish = true # do nothing - break if eos?
|
137
|
+
when Escape; ch = self.escaped; add ch
|
138
|
+
when "$"; dollar
|
139
|
+
when *FMTS; handle_simple_formatting(ch)
|
140
|
+
else grab_string
|
141
|
+
end
|
142
|
+
# api.tty "#{__method__}: AFTER CASE: api.data = #{api.data.inspect}"
|
143
|
+
[ch, finish, @token, @tokenlist]
|
144
|
+
end
|
145
|
+
|
146
|
+
def tokenize
|
147
|
+
ch = peek
|
148
|
+
loop do
|
149
|
+
ch = peek
|
150
|
+
stuff = grab_token(ch)
|
151
|
+
ch, finish, t, tlist = *stuff
|
152
|
+
break if finish
|
153
|
+
end
|
154
|
+
# api.tty "tokenize: i = #{self.i}"
|
155
|
+
# api.tty "tokenize: token = #{@token.inspect} tokenlist = #{@tokenlist.inspect}"
|
156
|
+
@tokenlist
|
157
|
+
end
|
158
|
+
|
159
|
+
# def self.get_vars
|
160
|
+
# grab
|
161
|
+
# case peek
|
162
|
+
# when LF, " ", nil
|
163
|
+
# add "$"
|
164
|
+
# add_token :str
|
165
|
+
# when "$"; double_dollar
|
166
|
+
## when "."; dollar_dot
|
167
|
+
# when /[A-Za-z]/
|
168
|
+
# add_token :str
|
169
|
+
# var = peek + grab_alpha_dot
|
170
|
+
# add_token(:var, var)
|
171
|
+
# else
|
172
|
+
# add "$" + peek
|
173
|
+
# add_token(:str)
|
174
|
+
# end
|
175
|
+
# end
|
176
|
+
#
|
177
|
+
# def self.parse_var_func # FIXME Hmm...
|
178
|
+
# loop do
|
179
|
+
# case peek
|
180
|
+
# when "$"
|
181
|
+
# dollar
|
182
|
+
# when LF
|
183
|
+
# break if eos?
|
184
|
+
# when nil
|
185
|
+
# break
|
186
|
+
# else
|
187
|
+
# add peek
|
188
|
+
# end
|
189
|
+
# grab
|
190
|
+
# end
|
191
|
+
# add_token(:str)
|
192
|
+
# @tokenlist
|
193
|
+
# end
|
194
|
+
|
195
|
+
def terminate?(terminators, ch)
|
196
|
+
if terminators.is_a? Regexp
|
197
|
+
terminators === ch
|
198
|
+
else
|
199
|
+
terminators.include?(ch)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def var_func_parse
|
204
|
+
char = self.peek
|
205
|
+
loop do
|
206
|
+
char = self.grab
|
207
|
+
break if char == LF || char == nil
|
208
|
+
self.escaped if char == Escape
|
209
|
+
self.dollar if char == "$" # Could be $$
|
210
|
+
self.add char
|
211
|
+
end
|
212
|
+
self.add_token(:str)
|
213
|
+
result = self.evaluate
|
214
|
+
result
|
215
|
+
end
|
216
|
+
|
217
|
+
def self.var_func_parse(str)
|
218
|
+
return nil if str.nil?
|
219
|
+
x = self.new(str.chomp)
|
220
|
+
char = x.peek
|
221
|
+
loop do
|
222
|
+
char = x.grab
|
223
|
+
break if char == LF || char == nil
|
224
|
+
x.escaped if char == Escape
|
225
|
+
x.dollar if char == "$" # Could be $$
|
226
|
+
x.add char
|
227
|
+
end
|
228
|
+
x.add_token(:str)
|
229
|
+
result = x.evaluate
|
230
|
+
result
|
231
|
+
end
|
232
|
+
|
233
|
+
def evaluate(tokens = @tokenlist)
|
234
|
+
@out = ""
|
235
|
+
return "" if tokens.empty?
|
236
|
+
gen = tokens.each
|
237
|
+
token = gen.next
|
238
|
+
loop do
|
239
|
+
break if token.nil?
|
240
|
+
sym, val = *token
|
241
|
+
case sym
|
242
|
+
when :str; eval_str(val)
|
243
|
+
when :var; eval_var(val)
|
244
|
+
when :func; eval_func(val, gen)
|
245
|
+
when *BITS; eval_bits(sym, val)
|
246
|
+
else
|
247
|
+
add_token :str
|
248
|
+
end
|
249
|
+
token = gen.next
|
250
|
+
end
|
251
|
+
@out
|
252
|
+
end
|
253
|
+
|
254
|
+
def add(str)
|
255
|
+
@token << str unless str.nil?
|
256
|
+
end
|
257
|
+
|
258
|
+
def add_token(kind, token = @token)
|
259
|
+
return if token.nil?
|
260
|
+
@tokenlist << [kind, token] unless token.empty?
|
261
|
+
@token = Null.dup
|
262
|
+
end
|
263
|
+
|
264
|
+
def grab_alpha
|
265
|
+
str = grab
|
266
|
+
loop do
|
267
|
+
break if eos?
|
268
|
+
break if terminate?(NoAlpha, peek)
|
269
|
+
str << grab
|
270
|
+
end
|
271
|
+
str
|
272
|
+
end
|
273
|
+
|
274
|
+
def grab_alpha_dot
|
275
|
+
str = grab # Null.dup
|
276
|
+
loop do
|
277
|
+
break if eos?
|
278
|
+
break if terminate?(NoAlphaDot, peek)
|
279
|
+
str << grab
|
280
|
+
end
|
281
|
+
str
|
282
|
+
end
|
283
|
+
|
284
|
+
def dollar
|
285
|
+
c1 = grab # $
|
286
|
+
c2 = grab # ...
|
287
|
+
case c2
|
288
|
+
when " "; add_token :str, "$ "
|
289
|
+
when LF, nil; add_token :str, "$"
|
290
|
+
when "$"; double_dollar
|
291
|
+
when "."; dollar_dot
|
292
|
+
when /[A-Za-z]/; add_token(:var, c2 + grab_alpha_dot)
|
293
|
+
else add_token(:str, "$" + c2)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
def finish_token(str, kind)
|
298
|
+
add str
|
299
|
+
add_token :str
|
300
|
+
grab
|
301
|
+
end
|
302
|
+
|
303
|
+
def marker(char)
|
304
|
+
add_token :str
|
305
|
+
sym = Syms[char]
|
306
|
+
return if embedded?
|
307
|
+
grab
|
308
|
+
case peek
|
309
|
+
when Space; finish_token(char + " ", :str)
|
310
|
+
when LF, nil; finish_token(char, :str)
|
311
|
+
when char; double_marker(char)
|
312
|
+
when LBrack; long_marker(char)
|
313
|
+
else
|
314
|
+
str = peek + collect!(sym, Blank)
|
315
|
+
add str
|
316
|
+
add_token sym, str
|
317
|
+
grab
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def double_marker(char)
|
322
|
+
sym = Syms[char]
|
323
|
+
kind = sym
|
324
|
+
case lookahead # first char after **
|
325
|
+
when Space, LF, nil
|
326
|
+
pre, post = SimpleFormats[sym]
|
327
|
+
add_token kind
|
328
|
+
else
|
329
|
+
str = collect!(sym, Punc)
|
330
|
+
add_token kind, str
|
331
|
+
grab
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def long_marker(char)
|
336
|
+
sym = Syms[char]
|
337
|
+
grab # skip left bracket
|
338
|
+
kind = sym # "param_#{sym}".to_sym
|
339
|
+
arg = collect!(sym, Param, true)
|
340
|
+
add_token kind, arg
|
341
|
+
end
|
342
|
+
|
343
|
+
def collect_bracketed(sym, terminators)
|
344
|
+
str = Null.dup # next is not " ","*","["
|
345
|
+
grab # ZZZ
|
346
|
+
loop do
|
347
|
+
if peek == Escape
|
348
|
+
grab
|
349
|
+
str << grab
|
350
|
+
next
|
351
|
+
end
|
352
|
+
if terminate?(terminators, peek)
|
353
|
+
break
|
354
|
+
end
|
355
|
+
str << peek # not a terminator
|
356
|
+
grab
|
357
|
+
end
|
358
|
+
|
359
|
+
if peek == "]" # skip right bracket
|
360
|
+
grab
|
361
|
+
end
|
362
|
+
add str
|
363
|
+
str
|
364
|
+
rescue => err
|
365
|
+
::STDERR.puts "ERR = #{err}\n#{err.backtrace}"
|
366
|
+
end
|
367
|
+
|
368
|
+
def escaped
|
369
|
+
grab # Eat the backslash
|
370
|
+
ch = grab # Take next char
|
371
|
+
ch
|
372
|
+
end
|
373
|
+
|
374
|
+
def collect!(sym, terminators, bracketed=nil)
|
375
|
+
return collect_bracketed(sym, terminators) if bracketed
|
376
|
+
str = Null.dup # next is not " ","*","["
|
377
|
+
grab # ZZZ
|
378
|
+
loop do
|
379
|
+
case
|
380
|
+
when peek.nil?
|
381
|
+
return str
|
382
|
+
when peek == Escape
|
383
|
+
str << escaped
|
384
|
+
next
|
385
|
+
when terminate?(terminators, peek)
|
386
|
+
break
|
387
|
+
else
|
388
|
+
str << peek # not a terminator
|
389
|
+
end
|
390
|
+
grab
|
391
|
+
end
|
392
|
+
ungrab
|
393
|
+
add str
|
394
|
+
str
|
395
|
+
rescue => err
|
396
|
+
::STDERR.puts "ERR = #{err}\n#{err.backtrace}"
|
397
|
+
end
|
398
|
+
|
399
|
+
def varsub(name)
|
400
|
+
live = Livetext.new
|
401
|
+
value = live.vars[name]
|
402
|
+
result = value || "[#{name} is undefined]"
|
403
|
+
result
|
404
|
+
end
|
405
|
+
|
406
|
+
def embedded?
|
407
|
+
! (['"', "'", " ", nil].include? prev)
|
408
|
+
end
|
409
|
+
|
410
|
+
# private
|
411
|
+
|
412
|
+
def eval_bits(sym, val)
|
413
|
+
# api.tty "eb: #{[sym, val].inspect}"
|
414
|
+
val = Livetext.interpolate(val)
|
415
|
+
@out << embed(sym, val)
|
416
|
+
end
|
417
|
+
|
418
|
+
def eval_func(val, gen)
|
419
|
+
param = nil
|
420
|
+
arg = gen.peek rescue :bogus
|
421
|
+
unless arg == :bogus
|
422
|
+
if [:colon, :brackets].include? arg[0]
|
423
|
+
arg = gen.next # for real
|
424
|
+
param = arg[1]
|
425
|
+
# FIXME - unsure - interpolate again??
|
426
|
+
# param = Livetext.interpolate(param)
|
427
|
+
end
|
428
|
+
end
|
429
|
+
str = funcall(val, param)
|
430
|
+
@out << str
|
431
|
+
end
|
432
|
+
|
433
|
+
def eval_var(val)
|
434
|
+
@out << varsub(val)
|
435
|
+
end
|
436
|
+
|
437
|
+
def eval_str(val)
|
438
|
+
@out << val unless val == "\n" # BUG
|
439
|
+
end
|
440
|
+
|
441
|
+
end
|