teepee 0.15.7 → 0.15.8

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.
@@ -0,0 +1,296 @@
1
+ # -*- coding: utf-8 -*-
2
+ # -*- mode: Ruby -*-
3
+
4
+ # Copyright © 2013-2016, Christopher Mark Gore,
5
+ # Soli Deo Gloria,
6
+ # All rights reserved.
7
+ #
8
+ # 2317 South River Road, Saint Charles, Missouri 63303 USA.
9
+ # Web: http://cgore.com
10
+ # Email: cgore@cgore.com
11
+ #
12
+ # Redistribution and use in source and binary forms, with or without
13
+ # modification, are permitted provided that the following conditions are met:
14
+ #
15
+ # * Redistributions of source code must retain the above copyright
16
+ # notice, this list of conditions and the following disclaimer.
17
+ #
18
+ # * Redistributions in binary form must reproduce the above copyright
19
+ # notice, this list of conditions and the following disclaimer in the
20
+ # documentation and/or other materials provided with the distribution.
21
+ #
22
+ # * Neither the name of Christopher Mark Gore nor the names of other
23
+ # contributors may be used to endorse or promote products derived from
24
+ # this software without specific prior written permission.
25
+ #
26
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
30
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36
+ # POSSIBILITY OF SUCH DAMAGE.
37
+
38
+
39
+ module Teepee
40
+ module CommanderMixins
41
+ module Mathematics
42
+ def numeric? *numbers
43
+ numbers.all? {|number| number.kind_of? Numeric}
44
+ end
45
+
46
+ def non_numeric_error nan
47
+ command_error "The value \"#{nan}\" is not a number."
48
+ end
49
+
50
+ def ensure_numeric number
51
+ if number.kind_of? Complex
52
+ command_error "Complex numbers are not yet supported."
53
+ elsif not number.kind_of? Numeric
54
+ command_error "Non-numeric result."
55
+ else
56
+ number
57
+ end
58
+ end
59
+
60
+ def number_from_word word
61
+ begin
62
+ word.to_number
63
+ rescue ArgumentError, NoMethodError
64
+ nil
65
+ end
66
+ end
67
+
68
+ def to_numbers words
69
+ words.map {|word| number_from_word word}.reject &:nil?
70
+ end
71
+
72
+ def + numbers
73
+ ensure_numeric to_numbers(numbers).inject 0, :+
74
+ end
75
+
76
+ def - numbers
77
+ numbers = to_numbers numbers
78
+ if numbers.length == 1
79
+ ensure_numeric -numbers.first
80
+ else
81
+ ensure_numeric numbers.reduce :-
82
+ end
83
+ end
84
+
85
+ def * numbers
86
+ ensure_numeric to_numbers(numbers).inject 1, :*
87
+ end
88
+
89
+ def / numbers
90
+ numbers = to_numbers numbers
91
+ if numbers.length == 1
92
+ ensure_numeric 1 / numbers.first
93
+ else
94
+ ensure_numeric numbers.reduce :/
95
+ end
96
+ end
97
+
98
+ def ** numbers
99
+ ensure_numeric to_numbers(numbers).reduce :**
100
+ end
101
+
102
+ def % numbers
103
+ to_numbers(numbers).inject { |base, percent| base*percent/100.0 }
104
+ end
105
+
106
+ def add_percentage numbers
107
+ to_numbers(numbers).inject {|base, percent| base * (1+percent/100.0) }
108
+ end
109
+
110
+ def subtract_percentage numbers
111
+ to_numbers(numbers).inject {|base, percent| base * (1-percent/100.0) }
112
+ end
113
+
114
+ def percent_total numbers
115
+ to_numbers(numbers).inject {|total, part| Float(part)/Float(total)*100.0 }
116
+ end
117
+
118
+ def acos angle
119
+ ensure_numeric Math.acos angle.to_number
120
+ end
121
+
122
+ def acosh angle
123
+ ensure_numeric Math.acosh angle.to_number
124
+ end
125
+
126
+ def asin angle
127
+ ensure_numeric Math.asin angle.to_number
128
+ end
129
+
130
+ def asinh angle
131
+ ensure_numeric Math.asinh angle.to_number
132
+ end
133
+
134
+ def atan angle
135
+ ensure_numeric Math.atan angle.to_number
136
+ end
137
+
138
+ def atanh angle
139
+ ensure_numeric Math.atanh angle.to_number
140
+ end
141
+
142
+ def ceiling number
143
+ ensure_numeric number.to_number.ceil
144
+ end
145
+
146
+ def cos angle
147
+ ensure_numeric Math.cos angle.to_number
148
+ end
149
+
150
+ def cosh angle
151
+ ensure_numeric Math.cosh angle.to_number
152
+ end
153
+
154
+ def degrees2radians degrees
155
+ ensure_numeric degrees.to_number * Math::PI / 180.0
156
+ end
157
+
158
+ def e
159
+ Math::E
160
+ end
161
+
162
+ def erf number
163
+ ensure_numeric Math.erf number.to_number
164
+ end
165
+
166
+ def erfc number
167
+ ensure_numeric Math.erfc number.to_number
168
+ end
169
+
170
+ def floor number
171
+ ensure_numeric number.to_number.floor
172
+ end
173
+
174
+ def gamma number
175
+ ensure_numeric Math.gamma number.to_number
176
+ end
177
+
178
+ def greater_than numbers
179
+ if numbers.empty?
180
+ true_constant
181
+ elsif numbers.length == 1
182
+ true_constant
183
+ else
184
+ numbers[0].to_number > numbers[1].to_number and greater_than numbers.rest
185
+ end
186
+ end
187
+
188
+ def greater_than_or_equal numbers
189
+ if numbers.empty?
190
+ true_constant
191
+ elsif numbers.length == 1
192
+ true_constant
193
+ else
194
+ numbers[0].to_number >= numbers[1].to_number and greater_than_or_equal numbers.rest
195
+ end
196
+ end
197
+
198
+ def i
199
+ command_error "Complex numbers are not yet supported."
200
+ end
201
+
202
+ def hypot numbers
203
+ ensure_numeric Math.sqrt to_numbers(numbers).map {|n| n**2}
204
+ end
205
+
206
+ def ld number
207
+ ensure_numeric Math.log2 number.to_number
208
+ end
209
+
210
+ def ldexp fraction, exponent
211
+ ensure_numeric Math.ldexp fraction.to_number, exponent.to_number
212
+ end
213
+
214
+ def less_than numbers
215
+ if numbers.empty?
216
+ true_constant
217
+ elsif numbers.length == 1
218
+ true_constant
219
+ else
220
+ numbers[0].to_number < numbers[1].to_number and less_than numbers.rest
221
+ end
222
+ end
223
+
224
+ def less_than_or_equal numbers
225
+ if numbers.empty?
226
+ true_constant
227
+ elsif numbers.length == 1
228
+ true_constant
229
+ else
230
+ numbers[0].to_number <= numbers[1].to_number and less_than_or_equal numbers.rest
231
+ end
232
+ end
233
+
234
+ def lgamma number
235
+ ensure_numeric Math::lgamma(number.to_number).first
236
+ end
237
+
238
+ def ln number
239
+ ensure_numeric Math.log number.to_number
240
+ end
241
+
242
+ def log base, number
243
+ if number.nil?
244
+ number, base = base, number
245
+ ensure_numeric Math.log10 number.to_number # default to log base 10
246
+ else
247
+ ensure_numeric Math.log number.to_number, base.to_number
248
+ end
249
+ end
250
+
251
+ def log10 number
252
+ ensure_numeric Math.log10 number.to_number
253
+ end
254
+
255
+ def mod numbers
256
+ ensure_numeric to_numbers(numbers).reduce :%
257
+ end
258
+
259
+ def pi
260
+ Math::PI
261
+ end
262
+
263
+ def radians2degrees radians
264
+ ensure_numeric(radians.to_number * 180.0 / Math::PI)
265
+ end
266
+
267
+ def round number, precision = nil, *_
268
+ if precision.nil? or precision.to_number.nil?
269
+ ensure_numeric number.to_number.round
270
+ else
271
+ ensure_numeric number.to_number.round precision.to_number
272
+ end
273
+ end
274
+
275
+ def sin angle
276
+ ensure_numeric Math.sin angle.to_number
277
+ end
278
+
279
+ def sinh angle
280
+ ensure_numeric Math.sinh angle.to_number
281
+ end
282
+
283
+ def sqrt number
284
+ ensure_numeric Math.sqrt number.to_number
285
+ end
286
+
287
+ def tan angle
288
+ ensure_numeric Math.tan angle.to_number
289
+ end
290
+
291
+ def tanh angle
292
+ ensure_numeric Math.tanh angle.to_number
293
+ end
294
+ end
295
+ end
296
+ end
@@ -0,0 +1,134 @@
1
+ # -*- coding: utf-8 -*-
2
+ # -*- mode: Ruby -*-
3
+
4
+ # Copyright © 2013-2016, Christopher Mark Gore,
5
+ # Soli Deo Gloria,
6
+ # All rights reserved.
7
+ #
8
+ # 2317 South River Road, Saint Charles, Missouri 63303 USA.
9
+ # Web: http://cgore.com
10
+ # Email: cgore@cgore.com
11
+ #
12
+ # Redistribution and use in source and binary forms, with or without
13
+ # modification, are permitted provided that the following conditions are met:
14
+ #
15
+ # * Redistributions of source code must retain the above copyright
16
+ # notice, this list of conditions and the following disclaimer.
17
+ #
18
+ # * Redistributions in binary form must reproduce the above copyright
19
+ # notice, this list of conditions and the following disclaimer in the
20
+ # documentation and/or other materials provided with the distribution.
21
+ #
22
+ # * Neither the name of Christopher Mark Gore nor the names of other
23
+ # contributors may be used to endorse or promote products derived from
24
+ # this software without specific prior written permission.
25
+ #
26
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
30
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36
+ # POSSIBILITY OF SUCH DAMAGE.
37
+
38
+
39
+ module Teepee
40
+ module CommanderMixins
41
+ module ThinkingBicycleModels
42
+ def tb_href target, string
43
+ %{<a href="#{TB_COM}/#{target}">#{string}</a>}
44
+ end
45
+
46
+ def id_command_handler(id,
47
+ klass,
48
+ singular = klass.to_s.camelcase_to_snakecase,
49
+ plural = singular.pluralize,
50
+ partial = "#{plural}/inline",
51
+ view="")
52
+ if not id
53
+ command_error "#{singular}_id: error: no #{singular} ID specified"
54
+ elsif not id.to_s =~ /\A[0-9]+\z/
55
+ command_error "#{singular}_id: error: invalid #{singular} ID specified"
56
+ else
57
+ tb_href "#{plural}/#{id.to_s}/#{view}", "#{klass.to_s} ##{id.to_s}"
58
+ end
59
+ end
60
+
61
+ def bookmarks_folder_id id
62
+ id_command_handler id, :Folder, "folder", "folders", "folders/bookmarks_inline", "bookmarks"
63
+ end
64
+
65
+ def folder_id id
66
+ id_command_handler id, :Folder
67
+ end
68
+
69
+ def forum_id id
70
+ id_command_handler id, :Forum
71
+ end
72
+
73
+ def image expressions
74
+ expressions = strip expressions
75
+ uri, *alt_text = expressions
76
+ uri = ERB::Util.html_escape uri.to_html
77
+ if not valid_uri? uri
78
+ command_error "Not a valid URI for the image."
79
+ else
80
+ if alt_text.empty?
81
+ html_tag :img, nil, {src: uri}
82
+ else
83
+ html_tag :img, nil, {src: uri, alt: alt_text.map(&:to_html).join.strip}
84
+ end
85
+ end
86
+ end
87
+
88
+ def keyword_id id
89
+ id_command_handler id, :Keyword
90
+ end
91
+
92
+ def link expressions
93
+ expressions = strip expressions
94
+ uri, *desc = expressions
95
+ uri = ERB::Util.html_escape uri.to_html
96
+ if not valid_uri? uri
97
+ command_error "Not a valid URI."
98
+ else
99
+ desc = [uri] if desc.empty?
100
+ html_tag :a, desc, {href: uri}
101
+ end
102
+ end
103
+
104
+ def link_id id
105
+ id_command_handler id, :Link
106
+ end
107
+
108
+ def mailto email_address
109
+ email_address = email_address.to_html
110
+ if valid_email_address? email_address
111
+ html_tag :a, [email_address], {href: "mailto:#{email_address}"}
112
+ else
113
+ command_error "I'm not sure that's a valid email address."
114
+ end
115
+ end
116
+
117
+ def note_id id
118
+ id_command_handler id, :Note
119
+ end
120
+
121
+ def tag_id id
122
+ id_command_handler id, :Tag
123
+ end
124
+
125
+ def user user
126
+ if not user
127
+ command_error "user: error: no user specified"
128
+ else
129
+ tb_href "users/#{user}", user.to_html
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -35,8 +35,20 @@
35
35
  # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36
36
  # POSSIBILITY OF SUCH DAMAGE.
37
37
 
38
+ require 'teepee/commander-mixins/boolean'
39
+ require 'teepee/commander-mixins/control'
40
+ require 'teepee/commander-mixins/formatting'
41
+ require 'teepee/commander-mixins/mathematics'
42
+ require 'teepee/commander-mixins/thinking-bicycle-models'
43
+
38
44
  module Teepee
39
45
  class Commander
46
+ include CommanderMixins::Boolean
47
+ include CommanderMixins::Control
48
+ include CommanderMixins::Formatting
49
+ include CommanderMixins::Mathematics
50
+ include CommanderMixins::ThinkingBicycleModels
51
+
40
52
  attr_accessor :parser
41
53
 
42
54
  def initialize params
@@ -61,73 +73,6 @@ module Teepee
61
73
  command_error "The variable \"#{variable}\" is not defined."
62
74
  end
63
75
 
64
- def numeric? *numbers
65
- numbers.all? {|number| number.kind_of? Numeric}
66
- end
67
-
68
- def non_numeric_error nan
69
- command_error "The value \"#{nan}\" is not a number."
70
- end
71
-
72
- def ensure_numeric number
73
- if number.kind_of? Complex
74
- command_error "Complex numbers are not yet supported."
75
- elsif not number.kind_of? Numeric
76
- command_error "Non-numeric result."
77
- else
78
- number
79
- end
80
- end
81
-
82
- def ensure_boolean boolean
83
- if boolean.to_html == "true" or boolean.to_html == "false"
84
- boolean
85
- else
86
- command_error "Non-boolean value."
87
- end
88
- end
89
-
90
- def html_tag tag, expressions, attribs=nil
91
- opening_tag = if attribs
92
- attribs_string = attribs.map {|k,v| %{#{k}="#{v}"}}.join " "
93
- if expressions.nil?
94
- "<#{tag} #{attribs_string}/>"
95
- else
96
- "<#{tag} #{attribs_string}>"
97
- end
98
- else
99
- if expressions.nil?
100
- "<#{tag}/>"
101
- else
102
- "<#{tag}>"
103
- end
104
- end
105
- if expressions.nil?
106
- opening_tag
107
- else
108
- opening_tag + expressions.map(&:to_html).join.strip + "</#{tag}>"
109
- end
110
- end
111
-
112
- def tb_href target, string
113
- %{<a href="#{TB_COM}/#{target}">#{string}</a>}
114
- end
115
-
116
- def id_command_handler(id,
117
- klass,
118
- singular = klass.to_s.camelcase_to_snakecase,
119
- plural = singular.pluralize,
120
- partial = "#{plural}/inline",
121
- view="")
122
- if not id
123
- command_error "#{singular}_id: error: no #{singular} ID specified"
124
- elsif not id.to_s =~ /\A[0-9]+\z/
125
- command_error "#{singular}_id: error: invalid #{singular} ID specified"
126
- else
127
- tb_href "#{plural}/#{id.to_s}/#{view}", "#{klass.to_s} ##{id.to_s}"
128
- end
129
- end
130
-
131
76
  def left_strip expressions
132
77
  while expressions.first.kind_of? WhitespaceToken
133
78
  expressions.shift
@@ -147,109 +92,13 @@ module Teepee
147
92
  end
148
93
 
149
94
  def valid_email_address? email_address
150
- email_address =~ /\A[[:graph:]]+@[\w.]+\z/
151
- end
152
-
153
- def true_constant? expression
154
- expression.to_html == "true"
155
- end
156
-
157
- def false_constant? expression
158
- expression.to_html == "false"
95
+ email_address =~ /\A[[:graph:]]+@[\w._-]+\z/
159
96
  end
160
97
 
161
98
  def pipe? expression
162
99
  expression.is_a? PipeToken
163
100
  end
164
101
 
165
- def number_from_word word
166
- begin
167
- word.to_number
168
- rescue ArgumentError, NoMethodError
169
- nil
170
- end
171
- end
172
-
173
- def to_numbers words
174
- words.map {|word| number_from_word word}.reject &:nil?
175
- end
176
-
177
- #----------------------------------------------------------------------------
178
-
179
- def + numbers
180
- ensure_numeric to_numbers(numbers).inject 0, :+
181
- end
182
-
183
- def - numbers
184
- numbers = to_numbers numbers
185
- if numbers.length == 1
186
- ensure_numeric -numbers.first
187
- else
188
- ensure_numeric numbers.reduce :-
189
- end
190
- end
191
-
192
- def * numbers
193
- ensure_numeric to_numbers(numbers).inject 1, :*
194
- end
195
-
196
- def / numbers
197
- numbers = to_numbers numbers
198
- if numbers.length == 1
199
- ensure_numeric 1 / numbers.first
200
- else
201
- ensure_numeric numbers.reduce :/
202
- end
203
- end
204
-
205
- def ** numbers
206
- ensure_numeric to_numbers(numbers).reduce :**
207
- end
208
-
209
- def % numbers
210
- to_numbers(numbers).inject { |base, percent| base*percent/100.0 }
211
- end
212
-
213
- def add_percentage numbers
214
- to_numbers(numbers).inject {|base, percent| base * (1+percent/100.0) }
215
- end
216
-
217
- def subtract_percentage numbers
218
- to_numbers(numbers).inject {|base, percent| base * (1-percent/100.0) }
219
- end
220
-
221
- def percent_total numbers
222
- to_numbers(numbers).inject {|total, part| Float(part)/Float(total)*100.0 }
223
- end
224
-
225
- def acos angle
226
- ensure_numeric Math.acos angle.to_number
227
- end
228
-
229
- def acosh angle
230
- ensure_numeric Math.acosh angle.to_number
231
- end
232
-
233
- def asin angle
234
- ensure_numeric Math.asin angle.to_number
235
- end
236
-
237
- def asinh angle
238
- ensure_numeric Math.asinh angle.to_number
239
- end
240
-
241
- def atan angle
242
- ensure_numeric Math.atan angle.to_number
243
- end
244
-
245
- def atanh angle
246
- ensure_numeric Math.atanh angle.to_number
247
- end
248
-
249
- def b expressions
250
- html_tag :b, expressions
251
- end
252
-
253
102
  def backquote
254
103
  "`"
255
104
  end
@@ -258,375 +107,10 @@ module Teepee
258
107
  "\\"
259
108
  end
260
109
 
261
- def big expressions
262
- html_tag :big, expressions
263
- end
264
-
265
- def bookmarks_folder_id id
266
- id_command_handler id, :Folder, "folder", "folders", "folders/bookmarks_inline", "bookmarks"
267
- end
268
-
269
- def boolean_and booleans
270
- if booleans.empty?
271
- return true_constant
272
- end
273
- b = booleans.first.to_html
274
- if false_constant? b
275
- false_constant
276
- elsif true_constant? b or booleans.first.kind_of? WhitespaceToken
277
- boolean_and booleans[1..-1]
278
- else
279
- command_error "Not a boolean value #{booleans.first}"
280
- end
281
- end
282
-
283
- def boolean_nand booleans
284
- boolean_not boolean_and booleans
285
- end
286
-
287
- def boolean_nor booleans
288
- boolean_not boolean_or booleans
289
- end
290
-
291
- def boolean_not boolean
292
- boolean = boolean.to_html
293
- if true_constant? boolean
294
- false_constant
295
- elsif false_constant? boolean
296
- true_constant
297
- else
298
- command_error "Not a boolean value"
299
- end
300
- end
301
-
302
- def boolean_or booleans
303
- if booleans.empty?
304
- return false_constant
305
- end
306
- b = booleans.first.to_html
307
- if true_constant? b
308
- true_constant
309
- elsif false_constant? b or booleans.first.kind_of? WhitespaceToken
310
- boolean_or booleans[1..-1]
311
- else
312
- command_error "Not a boolean value"
313
- end
314
- end
315
-
316
- def boolean_xnor booleans
317
- boolean_not boolean_xor booleans
318
- end
319
-
320
- def boolean_xor booleans
321
- # There are two schools of thought as to what a multi-variable XOR is.
322
- # 1. Chained XORs, giving a parity check.
323
- # 2. 'Exclusively' one true for ALL inputs.
324
- # I'm going with the second: one and only one true, the rest false.
325
- # It seems therefore that the zero-argument version should be false then.
326
- if booleans.empty?
327
- false_constant
328
- else
329
- any_trues = false
330
- booleans.each do |boolean|
331
- if true_constant? boolean
332
- if any_trues
333
- return false_constant
334
- else
335
- any_trues = true
336
- end
337
- elsif false_constant? boolean
338
- # do nothing
339
- elsif boolean.kind_of? WhitespaceToken
340
- # do nothing
341
- else
342
- return command_error "Not a boolean value"
343
- end
344
- end
345
- return any_trues.to_html
346
- end
347
- end
348
-
349
- def br
350
- html_tag :br, nil
351
- end
352
-
353
- def case_operator expressions
354
- value, _, *rest = strip expressions
355
- if value and not rest.empty?
356
- def cond_helper value, expressions
357
- test_value, _, form, *rest = strip expressions
358
- if equal [value.to_html, test_value.to_html]
359
- form
360
- elsif not rest.empty?
361
- cond_helper value, rest
362
- end
363
- end
364
- cond_helper value, rest
365
- end
366
- end
367
-
368
- def ceiling number
369
- ensure_numeric number.to_number.ceil
370
- end
371
-
372
- def comment expressions
373
- nil
374
- end
375
-
376
- def cond_operator expressions
377
- conditional, _, form, *rest = strip expressions
378
- if true_constant? conditional.to_html
379
- form
380
- elsif not rest.empty?
381
- cond_operator rest
382
- end
383
- end
384
-
385
- def cos angle
386
- ensure_numeric Math.cos angle.to_number
387
- end
388
-
389
- def cosh angle
390
- ensure_numeric Math.cosh angle.to_number
391
- end
392
-
393
- def decrement variable
394
- return variable_not_defined_error variable if not is_defined? variable
395
- old_value = get_operator(variable).to_number
396
- return non_numeric_error old_value if not numeric? old_value
397
- @parser.variables[variable.to_html] = old_value - 1
398
- end
399
-
400
- def define expressions
401
- variable, _, value = expressions
402
- k = variable.to_html
403
- v = value.to_html
404
- @parser.variables[k] = v
405
- get_operator k
406
- end
407
-
408
- def is_defined? variables
409
- if not variables.is_a? Array
410
- return is_defined? [variables]
411
- elsif Set.new(variables.map(&:to_html)).subset? Set.new(@parser.variables.keys)
412
- true_constant
413
- else
414
- false_constant
415
- end
416
- end
417
-
418
- def degrees2radians degrees
419
- ensure_numeric degrees.to_number * Math::PI / 180.0
420
- end
421
-
422
- def del expressions
423
- html_tag :del, expressions
424
- end
425
-
426
110
  def dollar
427
111
  "$"
428
112
  end
429
113
 
430
- def dotimes expressions
431
- n = expressions.first.to_number
432
- return "" if n.nil? or n < 1
433
- span_operator expressions[1..-1] * n
434
- end
435
-
436
- def e
437
- Math::E
438
- end
439
-
440
- def equal expressions
441
- if expressions.empty?
442
- true_constant
443
- elsif expressions.length == 1
444
- true_constant
445
- else
446
- expressions[0].to_html == expressions[1].to_html and equal expressions.rest
447
- end
448
- end
449
-
450
- def enumerate expressions
451
- html_tag :ol, expressions
452
- end
453
-
454
- def enumerate_numeric expressions
455
- html_tag :ol, expressions, {type: "1"}
456
- end
457
-
458
- def enumerate_uppercase expressions
459
- html_tag :ol, expressions, {type: "A"}
460
- end
461
-
462
- def enumerate_lowercase expressions
463
- html_tag :ol, expressions, {type: "a"}
464
- end
465
-
466
- def enumerate_roman_uppercase expressions
467
- html_tag :ol, expressions, {type: "I"}
468
- end
469
-
470
- def enumerate_roman_lowercase expressions
471
- html_tag :ol, expressions, {type: "i"}
472
- end
473
-
474
- def erf number
475
- ensure_numeric Math.erf number.to_number
476
- end
477
-
478
- def erfc number
479
- ensure_numeric Math.erfc number.to_number
480
- end
481
-
482
- def false_constant
483
- "false"
484
- end
485
-
486
- def floor number
487
- ensure_numeric number.to_number.floor
488
- end
489
-
490
- def folder_id id
491
- id_command_handler id, :Folder
492
- end
493
-
494
- def forum_id id
495
- id_command_handler id, :Forum
496
- end
497
-
498
- def gamma number
499
- ensure_numeric Math.gamma number.to_number
500
- end
501
-
502
- def get_operator variable
503
- @parser.variables[variable.to_html].to_html
504
- end
505
-
506
- def greater_than numbers
507
- if numbers.empty?
508
- true_constant
509
- elsif numbers.length == 1
510
- true_constant
511
- else
512
- numbers[0].to_number > numbers[1].to_number and greater_than numbers.rest
513
- end
514
- end
515
-
516
- def greater_than_or_equal numbers
517
- if numbers.empty?
518
- true_constant
519
- elsif numbers.length == 1
520
- true_constant
521
- else
522
- numbers[0].to_number >= numbers[1].to_number and greater_than_or_equal numbers.rest
523
- end
524
- end
525
-
526
- def h1 expressions
527
- html_tag :h1, expressions
528
- end
529
-
530
- def h2 expressions
531
- html_tag :h2, expressions
532
- end
533
-
534
- def h3 expressions
535
- html_tag :h3, expressions
536
- end
537
-
538
- def h4 expressions
539
- html_tag :h4, expressions
540
- end
541
-
542
- def h5 expressions
543
- html_tag :h5, expressions
544
- end
545
-
546
- def h6 expressions
547
- html_tag :h6, expressions
548
- end
549
-
550
- def i
551
- command_error "Complex numbers are not yet supported."
552
- end
553
-
554
- def if_operator expressions
555
- expressions = strip expressions
556
- conditional, _, true_clause, _, false_clause = expressions
557
- if true_constant? conditional.to_html
558
- true_clause.to_html
559
- elsif false_clause
560
- false_clause.to_html
561
- end
562
- end
563
-
564
- def image expressions
565
- expressions = strip expressions
566
- uri, *alt_text = expressions
567
- uri = ERB::Util.html_escape uri.to_html
568
- if not valid_uri? uri
569
- command_error "Not a valid URI for the image."
570
- else
571
- if alt_text.empty?
572
- html_tag :img, nil, {src: uri}
573
- else
574
- html_tag :img, nil, {src: uri, alt: alt_text.map(&:to_html).join.strip}
575
- end
576
- end
577
- end
578
-
579
- def increment variable
580
- return variable_not_defined_error variable if not is_defined? variable
581
- old_value = get_operator(variable).to_number
582
- return non_numeric_error old_value if not numeric? old_value
583
- @parser.variables[variable.to_html] = old_value + 1
584
- end
585
-
586
- def it expressions
587
- html_tag :i, expressions
588
- end
589
-
590
- def item expressions
591
- html_tag :li, expressions
592
- end
593
-
594
- def itemize expressions
595
- html_tag :ul, expressions
596
- end
597
-
598
- def itemize_disc expressions
599
- html_tag :ul, expressions, {style: "list-style-type:disc"}
600
- end
601
-
602
- def itemize_circle expressions
603
- html_tag :ul, expressions, {style: "list-style-type:circle"}
604
- end
605
-
606
- def itemize_square expressions
607
- html_tag :ul, expressions, {style: "list-style-type:square"}
608
- end
609
-
610
- def itemize_none expressions
611
- html_tag :ul, expressions, {style: "list-style-type:none"}
612
- end
613
-
614
- def hypot numbers
615
- ensure_numeric Math.sqrt to_numbers(numbers).map {|n| n**2}
616
- end
617
-
618
- def keyword_id id
619
- id_command_handler id, :Keyword
620
- end
621
-
622
- def ld number
623
- ensure_numeric Math.log2 number.to_number
624
- end
625
-
626
- def ldexp fraction, exponent
627
- ensure_numeric Math.ldexp fraction.to_number, exponent.to_number
628
- end
629
-
630
114
  def left_brace
631
115
  "{"
632
116
  end
@@ -635,123 +119,10 @@ module Teepee
635
119
  "["
636
120
  end
637
121
 
638
- def less_than numbers
639
- if numbers.empty?
640
- true_constant
641
- elsif numbers.length == 1
642
- true_constant
643
- else
644
- numbers[0].to_number < numbers[1].to_number and less_than numbers.rest
645
- end
646
- end
647
-
648
- def less_than_or_equal numbers
649
- if numbers.empty?
650
- true_constant
651
- elsif numbers.length == 1
652
- true_constant
653
- else
654
- numbers[0].to_number <= numbers[1].to_number and less_than_or_equal numbers.rest
655
- end
656
- end
657
-
658
- def lgamma number
659
- ensure_numeric Math::lgamma(number.to_number).first
660
- end
661
-
662
- def link expressions
663
- expressions = strip expressions
664
- uri, *desc = expressions
665
- uri = ERB::Util.html_escape uri.to_html
666
- if not valid_uri? uri
667
- command_error "Not a valid URI."
668
- else
669
- desc = [uri] if desc.empty?
670
- html_tag :a, desc, {href: uri}
671
- end
672
- end
673
-
674
- def link_id id
675
- id_command_handler id, :Link
676
- end
677
-
678
- def ln number
679
- ensure_numeric Math.log number.to_number
680
- end
681
-
682
- def log base, number
683
- if number.nil?
684
- number, base = base, number
685
- ensure_numeric Math.log10 number.to_number # default to log base 10
686
- else
687
- ensure_numeric Math.log number.to_number, base.to_number
688
- end
689
- end
690
-
691
- def log10 number
692
- ensure_numeric Math.log10 number.to_number
693
- end
694
-
695
- def mailto email_address
696
- email_address = email_address.to_html
697
- if valid_email_address? email_address
698
- html_tag :a, [email_address], {href: "mailto:#{email_address}"}
699
- else
700
- command_error "I'm not sure that's a valid email address."
701
- end
702
- end
703
-
704
- def mod numbers
705
- ensure_numeric to_numbers(numbers).reduce :%
706
- end
707
-
708
- def nbsp count
709
- if count and count.to_number and count.to_number > 0
710
- "&nbsp;" * count.to_number
711
- else
712
- "&nbsp;"
713
- end
714
- end
715
-
716
- def not_equal numbers
717
- if numbers.empty?
718
- true_constant
719
- elsif numbers.length == 1
720
- true_constant
721
- else
722
- numbers[0].to_number != numbers[1].to_number and equal numbers.rest
723
- end
724
- end
725
-
726
- def note_id id
727
- id_command_handler id, :Note
728
- end
729
-
730
- def pi
731
- Math::PI
732
- end
733
-
734
122
  def pipe
735
123
  "|"
736
124
  end
737
125
 
738
- def prog1_operator expressions
739
- expressions.map(&:to_html).first
740
- end
741
-
742
- def progn_operator expressions
743
- expressions.map(&:to_html).last
744
- end
745
-
746
- def prognil expressions
747
- expressions.map(&:to_html)
748
- ""
749
- end
750
-
751
- def radians2degrees radians
752
- ensure_numeric(radians.to_number * 180.0 / Math::PI)
753
- end
754
-
755
126
  def right_brace
756
127
  "}"
757
128
  end
@@ -760,129 +131,12 @@ module Teepee
760
131
  "]"
761
132
  end
762
133
 
763
- def round number, precision = nil, *_
764
- if precision.nil? or precision.to_number.nil?
765
- ensure_numeric number.to_number.round
766
- else
767
- ensure_numeric number.to_number.round precision.to_number
768
- end
769
- end
770
-
771
- def sin angle
772
- ensure_numeric Math.sin angle.to_number
773
- end
774
-
775
- def sinh angle
776
- ensure_numeric Math.sinh angle.to_number
777
- end
778
-
779
- def small expressions
780
- html_tag :small, expressions
781
- end
782
-
783
134
  def squiggle
784
135
  "~"
785
136
  end
786
137
 
787
- def sqrt number
788
- ensure_numeric Math.sqrt number.to_number
789
- end
790
-
791
138
  def space
792
139
  " "
793
140
  end
794
-
795
- def span_operator expressions
796
- html_tag :span, expressions
797
- end
798
-
799
- def sub expressions
800
- html_tag :sub, expressions
801
- end
802
-
803
- def sup expressions
804
- html_tag :sup, expressions
805
- end
806
-
807
- def table expressions
808
- html_tag :table, expressions
809
- end
810
-
811
- def table_data expressions
812
- html_tag :td, expressions
813
- end
814
-
815
- def table_header expressions
816
- html_tag :th, expressions
817
- end
818
-
819
- def table_row expressions
820
- html_tag :tr, expressions
821
- end
822
-
823
- def tag_id id
824
- id_command_handler id, :Tag
825
- end
826
-
827
- def tan angle
828
- ensure_numeric Math.tan angle.to_number
829
- end
830
-
831
- def tanh angle
832
- ensure_numeric Math.tanh angle.to_number
833
- end
834
-
835
- def true_constant
836
- "true"
837
- end
838
-
839
- def tt expressions
840
- html_tag :tt, expressions
841
- end
842
-
843
- def u expressions
844
- html_tag :u, expressions
845
- end
846
-
847
- def undefine expressions
848
- expressions.each do |expression|
849
- @parser.variables.delete expression.to_html
850
- end
851
- ""
852
- end
853
-
854
- def unless_operator expressions
855
- expressions = strip expressions
856
- conditional = expressions.first
857
- expressions = strip expressions.rest
858
- if false_constant? conditional.to_html
859
- if expressions.length <= 1
860
- expressions.first
861
- else
862
- span_operator expressions
863
- end
864
- end
865
- end
866
-
867
- def user user
868
- if not user
869
- command_error "user: error: no user specified"
870
- else
871
- tb_href "users/#{user}", user.to_html
872
- end
873
- end
874
-
875
- def when_operator expressions
876
- expressions = strip expressions
877
- conditional = expressions.first
878
- expressions = strip expressions.rest
879
- if true_constant? conditional.to_html
880
- if expressions.length <= 1
881
- expressions.first
882
- else
883
- span_operator expressions
884
- end
885
- end
886
- end
887
141
  end
888
142
  end