livetext 0.8.77 → 0.8.78

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.
@@ -1,297 +1,322 @@
1
1
  class FormatLine
2
-
3
- EOL = :eol
4
- Alpha = /[a-z]/
5
- Alpha2 = /[a-z0-9_]/
6
- Other = Object
7
-
8
2
  SimpleFormats = {}
9
3
  SimpleFormats[:b] = %w[<b> </b>]
10
4
  SimpleFormats[:i] = %w[<i> </i>]
11
5
  SimpleFormats[:t] = ["<font size=+1><tt>", "</tt></font>"]
12
6
  SimpleFormats[:s] = %w[<strike> </strike>]
7
+
8
+ Null = ""
9
+ Space = " "
10
+ Alpha = /[A-Za-z]/
11
+ AlNum = /[A-Za-z0-9_]/
12
+ LF = "\n"
13
+ LBrack = "["
14
+
15
+ Blank = [" ", nil, "\n"]
16
+ Punc = [")", ",", ".", " ", "\n"]
17
+ NoAlpha = /[^A-Za-z0-9_]/
18
+ Param = ["]", "\n", nil]
19
+ Escape = "\\" # not an ESC char
13
20
 
14
- def initialize
15
- @buffer, @vname, @fname, @param, @substr = "", "", "", "", ""
21
+ def terminate?(terminators, ch)
22
+ if terminators.is_a? Regexp
23
+ terminators === ch
24
+ else
25
+ terminators.include?(ch)
26
+ end
16
27
  end
17
28
 
18
- def peek
19
- @enum.peek
20
- rescue StopIteration
21
- EOL
29
+ attr_reader :out
30
+
31
+ def initialize(line, context)
32
+ context ||= binding
33
+ @context = context
34
+ @line = line
35
+ @i = -1
36
+ @token = Null.dup
37
+ @tokenlist = []
22
38
  end
23
39
 
24
- def grab
25
- @enum.next
26
- rescue StopIteration
27
- EOL
40
+ def self.parse!(line, context = nil)
41
+ x = self.new(line.chomp, context)
42
+ x.tokenize(line)
43
+ x.evaluate # (context)
28
44
  end
29
45
 
30
- def skip
31
- @enum.next
32
- @enum.peek
33
- rescue StopIteration
34
- EOL
46
+ def tokenize(line)
47
+ grab
48
+ loop do
49
+ case curr
50
+ when Escape; go; add curr; grab
51
+ when "$"
52
+ _dollar
53
+ when "*", "_", "`", "~"
54
+ marker curr
55
+ add curr
56
+ when LF, nil
57
+ break
58
+ else
59
+ add curr
60
+ end
61
+ grab
62
+ end
63
+ add_token(:str)
64
+ @tokenlist
35
65
  end
36
66
 
37
- def keep(initial = "")
38
- @buffer << initial
39
- @buffer << @enum.next
40
- rescue StopIteration
41
- EOL
67
+ def embed(sym, str)
68
+ pre, post = SimpleFormats[sym]
69
+ pre + str + post
42
70
  end
43
71
 
44
- def emit(str = "")
45
- @buffer << str
72
+ def evaluate(tokens = @tokenlist)
73
+ @out = ""
74
+ return "" if tokens.empty?
75
+ gen = tokens.each
76
+ token = gen.next
77
+ loop do
78
+ break if token.nil?
79
+ sym, val = *token
80
+ case sym
81
+ when :str
82
+ @out << val unless val == "\n" # BUG
83
+ when :var
84
+ @out << varsub(val)
85
+ when :func
86
+ param = nil
87
+ arg = gen.peek
88
+ if [:colon, :brackets].include? arg[0]
89
+ arg = gen.next # for real
90
+ param = arg[1]
91
+ end
92
+ @out << funcall(val, param)
93
+ when :b, :i, :t, :s
94
+ @out << embed(sym, val)
95
+ else
96
+ add_token :str
97
+ end
98
+ token = gen.next
99
+ end
100
+ @out
46
101
  end
47
102
 
48
- def funcall(name, param)
49
- if self.respond_to?("func_" + name.to_s)
50
- self.send("func_" + name.to_s, param)
51
- else
52
- fobj = ::Livetext::Functions.new
53
- ::Livetext::Functions.param = param # is this
54
- ::Livetext::Functions.context = @context # screwed up???
55
- fobj.send(name)
103
+ def curr
104
+ @line[@i]
105
+ end
106
+
107
+ def prev
108
+ @line[@i-1]
109
+ end
110
+
111
+ def next!
112
+ @line[@i+1]
113
+ end
114
+
115
+ def grab
116
+ @line[@i+=1]
117
+ end
118
+
119
+ def grab_colon_param
120
+ grab # grab :
121
+ param = ""
122
+ loop do
123
+ case next!
124
+ when Escape
125
+ grab
126
+ param << next!
127
+ grab
128
+ when Space, LF, nil; break
129
+ else
130
+ param << next!
131
+ grab
132
+ end
133
+ end
134
+
135
+ param = nil if param.empty?
136
+ param
137
+ end
138
+
139
+ def grab_func_param
140
+ grab # [
141
+ param = ""
142
+ loop do
143
+ case next!
144
+ when Escape
145
+ grab
146
+ param << next!
147
+ grab
148
+ when "]", LF, nil; break
149
+ else
150
+ param << next!
151
+ grab
152
+ end
56
153
  end
154
+
155
+ add curr
156
+ grab
157
+ param = nil if param.empty?
158
+ param
159
+ end
160
+
161
+ def add(str)
162
+ @token << str unless str.nil?
57
163
  end
58
164
 
59
- def vsub
60
- @buffer << Livetext::Vars[@vname]
61
- @vname = ""
165
+ Syms = { "*" => :b, "_" => :i, "`" => :t, "~" => :s }
166
+
167
+ def add_token(kind, token = @token)
168
+ @tokenlist << [kind, token] unless token.empty?
169
+ @token = Null.dup
62
170
  end
63
171
 
64
- def fcall
65
- @buffer << funcall(@fname, @param)
66
- @fname, @param = "", ""
172
+ def grab_alpha
173
+ str = Null.dup
174
+ grab
175
+ loop do
176
+ break if curr.nil?
177
+ str << curr
178
+ break if terminate?(NoAlpha, next!)
179
+ grab
180
+ end
181
+ str
67
182
  end
68
183
 
69
- # FIXME Much of this should be done via CSS
70
- # FIXME In particular, strike is deprecated.
184
+ def _dollar
185
+ grab
186
+ case curr
187
+ when LF; add "$"; add_token :str
188
+ when " "; add "$ "; add_token :str
189
+ when nil; add "$"; add_token :str
190
+ when "$"; _double_dollar
191
+ when "."; _dollar_dot
192
+ when /[A-Za-z]/
193
+ add_token :str
194
+ var = curr + grab_alpha
195
+ add_token(:var, var)
196
+ else
197
+ add "$" + curr
198
+ add_token(:string)
199
+ end
200
+ end
71
201
 
72
- def bold
73
- d0, d1 = SimpleFormats[:b]
74
- @buffer << "#{d0}#@substr#{d1}"
75
- @substr = ""
202
+ def _double_dollar
203
+ case next!
204
+ when Space; add_token :string, "$$ "; grab; return
205
+ when LF, nil; add "$$"; add_token :str
206
+ when Alpha
207
+ add_token(:str, @token)
208
+ func = grab_alpha
209
+ add_token(:func, func)
210
+ case next!
211
+ when ":"; param = grab_colon_param; add_token(:colon, param)
212
+ when "["; param = grab_func_param; add_token(:brackets, param)
213
+ else # do nothing
214
+ end
215
+ else
216
+ grab; add_token :str, "$$" + curr; return
217
+ end
76
218
  end
77
219
 
78
- def ttype
79
- d0, d1 = SimpleFormats[:t]
80
- @buffer << "#{d0}#@substr#{d1}"
81
- @substr = ""
220
+ def dollar_dot
221
+ add_token :ddot, @line[@i..-1]
82
222
  end
83
223
 
84
- def italics
85
- d0, d1 = SimpleFormats[:i]
86
- @buffer << "#{d0}#@substr#{d1}"
87
- @substr = ""
224
+ def marker(char)
225
+ add_token :str
226
+ sym = Syms[char]
227
+ if embedded?
228
+ add char # ??? add_token "*", :string
229
+ return
230
+ end
231
+ grab
232
+ case curr
233
+ when Space
234
+ add char + " "
235
+ add_token :str
236
+ grab
237
+ when LF, nil
238
+ add char
239
+ add_token :str
240
+ when char; double_marker(char)
241
+ when LBrack; long_marker(char)
242
+ else
243
+ add curr
244
+ str = collect!(sym, Blank)
245
+ add_token sym, str
246
+ add curr # next char onto next token...
247
+ end
88
248
  end
89
249
 
90
- def strike
91
- d0, d1 = SimpleFormats[:s]
92
- @buffer << "#{d0}#@substr#{d1}"
93
- @substr = ""
250
+ def double_marker(char)
251
+ sym = Syms[char]
252
+ grab
253
+ kind = sym # "string_#{char}".to_sym
254
+ case next! # first char after **
255
+ when Space, LF, nil
256
+ pre, post = SimpleFormats[sym]
257
+ add_token kind
258
+ else
259
+ str = collect!(sym, Punc)
260
+ grab unless next!.nil?
261
+ add_token kind, str
262
+ end
94
263
  end
95
264
 
96
- def parse(line, context = nil)
97
- context ||= binding
98
- @context = context
99
- @enum = line.chomp.each_char
100
- @buffer = ""
101
- @substr = ""
102
- @fname = ""
103
- @vname = ""
104
- @param = ""
105
-
106
- # FIXME - refactor, generalize, clarify
107
-
108
- loop do # starting state
109
- char = peek
110
- case char
111
- when "\\"
112
- char = skip
113
- case char
114
- when "$", "*", "_", "`", "~"
115
- emit(char)
116
- skip
117
- when " "
118
- emit("\\ ")
119
- skip
120
- when EOL
121
- emit("\\")
122
- break
123
- when Other
124
- emit("\\") # logic??
125
- end
126
- when EOL
127
- break
128
- when "$" # var or func or $
129
- case skip
130
- when EOL
131
- emit("$")
132
- break
133
- when Alpha
134
- loop { ch = peek; break if ch == EOL; @vname << grab; break unless Alpha2 === peek }
135
- vsub
136
- when "$"
137
- case skip
138
- when EOL
139
- emit("$$")
140
- break
141
- when Alpha
142
- loop { ch = peek; break if ch == EOL; @fname << grab; break unless Alpha2 === peek }
143
- case peek
144
- when " " # no param - just call
145
- @param = nil
146
- fcall # no param? Hmm
147
- when "[" # long param
148
- skip
149
- loop do
150
- if peek == "\\"
151
- skip
152
- @param << grab
153
- end
154
- break if ["]", EOL].include?(peek)
155
- @param << grab
156
- end
157
- skip
158
- fcall
159
- when ":" # param (single token or to-eol)
160
- case skip
161
- when ":" # param to eol
162
- skip
163
- loop { break if peek == EOL; @param << grab }
164
- when Other # grab until space or eol
165
- loop { @param << grab; break if [" ", EOL].include?(peek) }
166
- fcall
167
- end
168
- when Other # no param - just call
169
- fcall
170
- end
171
- when Other
172
- emit "$$"
173
- end
174
- when Other
175
- emit "$"
176
- end
177
- when "*"
178
- case skip
179
- when EOL
180
- emit "*"
181
- when " "
182
- emit "*"
183
- when "["
184
- skip
185
- loop do
186
- if peek == "\\"
187
- skip
188
- @substr << grab
189
- next
190
- end
191
- break if ["]", EOL].include?(peek)
192
- @substr << grab
193
- end
194
- skip
195
- bold
196
- when Other
197
- loop { @substr << grab; break if [" ", EOL].include?(peek) }
198
- bold
199
- end
200
- when "_"
201
- case skip
202
- when EOL
203
- emit "_"
204
- when " "
205
- emit "_"
206
- when "["
207
- skip
208
- loop do
209
- if peek == "\\"
210
- skip
211
- @substr << grab
212
- next
213
- end
214
- break if ["]", EOL].include?(peek)
215
- @substr << grab
216
- end
217
- skip
218
- italics
219
- when "_" # doubled...
220
- skip
221
- loop do
222
- if peek == "\\"
223
- skip
224
- @substr << grab
225
- next
226
- end
227
- break if [".", ",", ")", EOL].include?(peek)
228
- @substr << grab
229
- end
230
- italics
231
- when Other
232
- loop { @substr << grab; break if [" ", EOL].include?(peek) }
233
- italics
234
- end
235
- when "`"
236
- case skip
237
- when EOL
238
- emit "`"
239
- when " "
240
- emit "`"
241
- when "["
242
- skip
243
- loop do
244
- if peek == "\\"
245
- skip
246
- @substr << grab
247
- next
248
- end
249
- break if ["]", EOL].include?(peek)
250
- @substr << grab
251
- end
252
- skip
253
- ttype
254
- when "`" # doubled...
255
- skip
256
- loop { break if [".", ",", ")", EOL].include?(peek); @substr << grab } # ";" ?? FIXME
257
- ttype
258
- when Other
259
- loop { @substr << grab; break if [" ", EOL].include?(peek) }
260
- ttype
261
- end
262
- when "~"
263
- case skip
264
- when EOL
265
- emit "~"
266
- when " "
267
- emit "~"
268
- when "["
269
- skip
270
- loop do
271
- if peek == "\\"
272
- skip
273
- @substr << grab
274
- next
275
- end
276
- break if ["]", EOL].include?(peek)
277
- @substr << grab
278
- end
279
- skip
280
- strike
281
- when "~" # doubled...
282
- skip
283
- loop { break if [".", ",", ")", EOL].include?(peek); @substr << grab } # ";" ?? FIXME
284
- strike
285
- when Other
286
- loop { @substr << grab; break if [" ", EOL].include?(peek) }
287
- strike
288
- end
289
- when Other
290
- keep
265
+ def long_marker(char)
266
+ sym = Syms[char]
267
+ # grab # skip left bracket
268
+ kind = sym # "param_#{sym}".to_sym
269
+ arg = collect!(sym, Param, true)
270
+ add_token kind, arg
271
+ end
272
+
273
+ def collect!(sym, terminators, param=false)
274
+ str = Null.dup # next is not " ","*","["
275
+ grab
276
+ loop do
277
+ if curr == Escape
278
+ str << grab # ch = escaped char
279
+ grab
280
+ next
291
281
  end
282
+ break if terminate?(terminators, curr)
283
+ str << curr # not a terminator
284
+ grab
292
285
  end
286
+ grab if param && curr == "]" # skip right bracket
287
+ add str
288
+ end
293
289
 
294
- @buffer
290
+ ############
291
+
292
+ ### From FormatLine:
293
+
294
+ def funcall(name, param)
295
+ result =
296
+ if self.respond_to?("func_" + name.to_s)
297
+ self.send("func_" + name.to_s, param)
298
+ else
299
+ fobj = ::Livetext::Functions.new
300
+ ::Livetext::Functions.param = param # is this
301
+ ::Livetext::Functions.context = @context # screwed up???
302
+ fobj.send(name)
303
+ end
304
+ result
295
305
  end
296
- end
297
306
 
307
+ def varsub(name)
308
+ result = Livetext::Vars[name]
309
+ result
310
+ end
311
+
312
+ #####
313
+
314
+ def showme(tag)
315
+ char = @line[@cc]
316
+ puts "--- #{tag}: ch=#{@ch.inspect} next=#{@next.inspect} (cc=#@cc:#{char.inspect}) out=#{@out.inspect}"
317
+ end
318
+
319
+ def embedded?
320
+ ! (['"', "'", " ", nil].include? prev)
321
+ end
322
+ end