1if not modules then modules = { } end modules [ ' util-str ' ] = {
2 version = 1 . 001 ,
3 comment = " companion to luat-lib.mkiv " ,
4 author = " Hans Hagen, PRAGMA-ADE, Hasselt NL " ,
5 copyright = " PRAGMA ADE / ConTeXt Development Team " ,
6 license = " see context related readme files "
7}
8
9utilities = utilities or { }
10utilities . strings = utilities . strings or { }
11local strings = utilities . strings
12
13local format , gsub , rep , sub , find = string . format , string . gsub , string . rep , string . sub , string . find
14local load , dump = load , string . dump
15local tonumber , type , tostring , next , setmetatable = tonumber , type , tostring , next , setmetatable
16local unpack , concat = table . unpack , table . concat
17local P , V , C , S , R , Ct , Cs , Cp , Carg , Cc = lpeg . P , lpeg . V , lpeg . C , lpeg . S , lpeg . R , lpeg . Ct , lpeg . Cs , lpeg . Cp , lpeg . Carg , lpeg . Cc
18local patterns , lpegmatch = lpeg . patterns , lpeg . match
19local utfchar , utfbyte , utflen = utf . char , utf . byte , utf . len
20
21
22
23
24local loadstripped = function ( str , shortcuts )
25 if shortcuts then
26 return load ( dump ( load ( str ) , true ) , nil , nil , shortcuts )
27 else
28 return load ( dump ( load ( str ) , true ) )
29 end
30end
31
32
33
34if not number then number = { } end
35
36local stripzero = patterns . stripzero
37local stripzeros = patterns . stripzeros
38local newline = patterns . newline
39local endofstring = patterns . endofstring
40local anything = patterns . anything
41local whitespace = patterns . whitespace
42local space = patterns . space
43local spacer = patterns . spacer
44local spaceortab = patterns . spaceortab
45local digit = patterns . digit
46local sign = patterns . sign
47local period = patterns . period
48
49
50
51
52
53
54
55
56
57
58
59local ptf = 1 / 65536
60local bpf = ( 7200 / 7227 ) / 65536
61
62local function points ( n )
63 if n = = 0 then
64 return " 0pt "
65 end
66 n = tonumber ( n )
67 if not n or n = = 0 then
68 return " 0pt "
69 end
70 n = n * ptf
71 if n % 1 = = 0 then
72 return format ( " %ipt " , n )
73 end
74 return lpegmatch ( stripzeros , format ( " %.5fpt " , n ) )
75end
76
77local function basepoints ( n )
78 if n = = 0 then
79 return " 0pt "
80 end
81 n = tonumber ( n )
82 if not n or n = = 0 then
83 return " 0pt "
84 end
85 n = n * bpf
86 if n % 1 = = 0 then
87 return format ( " %ibp " , n )
88 end
89 return lpegmatch ( stripzeros , format ( " %.5fbp " , n ) )
90end
91
92number . points = points
93number . basepoints = basepoints
94
95
96
97
98local rubish = spaceortab ^ 0 * newline
99local anyrubish = spaceortab + newline
100local stripped = ( spaceortab ^ 1 / " " ) * newline
101local leading = rubish ^ 0 / " "
102local trailing = ( anyrubish ^ 1 * endofstring ) / " "
103local redundant = rubish ^ 3 / " \n "
104
105local pattern = Cs ( leading * ( trailing + redundant + stripped + anything ) ^ 0 )
106
107function strings . collapsecrlf ( str )
108 return lpegmatch ( pattern , str )
109end
110
111
112
113local repeaters = { }
114
115function strings . newrepeater ( str , offset )
116 offset = offset or 0
117 local s = repeaters [ str ]
118 if not s then
119 s = { }
120 repeaters [ str ] = s
121 end
122 local t = s [ offset ]
123 if t then
124 return t
125 end
126 t = { }
127 setmetatable ( t , { __index = function ( t , k )
128 if not k then
129 return " "
130 end
131 local n = k + offset
132 local s = n > 0 and rep ( str , n ) or " "
133 t [ k ] = s
134 return s
135 end } )
136 s [ offset ] = t
137 return t
138end
139
140
141
142
143local extra , tab , start = 0 , 0 , 4 , 0
144
145local nspaces = strings . newrepeater ( " " )
146
147string . nspaces = nspaces
148
149local pattern =
150 Carg ( 1 ) / function ( t )
151 extra , tab , start = 0 , t or 7 , 1
152 end
153 * Cs ( (
154 Cp ( ) * patterns . tab / function ( position )
155 local current = ( position - start + 1 ) + extra
156 local spaces = tab - ( current -1 ) % tab
157 if spaces > 0 then
158 extra = extra + spaces - 1
159 return nspaces [ spaces ]
160 else
161 return " "
162 end
163 end
164 + newline * Cp ( ) / function ( position )
165 extra , start = 0 , position
166 end
167 + anything
168 ) ^ 1 )
169
170function strings . tabtospace ( str , tab )
171
172 return lpegmatch ( pattern , str , 1 , tab or 7 )
173end
174
175function string . utfpadding ( s , n )
176 if not n or n = = 0 then
177 return " "
178 end
179 local l = utflen ( s )
180 if n > 0 then
181 return nspaces [ n - l ]
182 else
183 return nspaces [ - n - l ]
184 end
185end
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210local optionalspace = spacer ^ 0
211local nospace = optionalspace / " "
212local endofline = nospace * newline
213
214local stripend = ( whitespace ^ 1 * endofstring ) / " "
215
216local normalline = ( nospace * ( ( 1 - optionalspace * ( newline + endofstring ) ) ^ 1 ) * nospace )
217
218local stripempty = endofline ^ 1 / " "
219local normalempty = endofline ^ 1
220local singleempty = endofline * ( endofline ^ 0 / " " )
221local doubleempty = endofline * endofline ^ -1 * ( endofline ^ 0 / " " )
222local stripstart = stripempty ^ 0
223
224local intospace = whitespace ^ 1 / " "
225local noleading = whitespace ^ 1 / " "
226local notrailing = noleading * endofstring
227
228local p_prune_normal = Cs ( stripstart * ( stripend + normalline + normalempty ) ^ 0 )
229local p_prune_collapse = Cs ( stripstart * ( stripend + normalline + doubleempty ) ^ 0 )
230local p_prune_noempty = Cs ( stripstart * ( stripend + normalline + singleempty ) ^ 0 )
231local p_prune_intospace = Cs ( noleading * ( notrailing + intospace + 1 ) ^ 0 )
232local p_retain_normal = Cs ( ( normalline + normalempty ) ^ 0 )
233local p_retain_collapse = Cs ( ( normalline + doubleempty ) ^ 0 )
234local p_retain_noempty = Cs ( ( normalline + singleempty ) ^ 0 )
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256local striplinepatterns = {
257 [ " prune " ] = p_prune_normal ,
258 [ " prune and collapse " ] = p_prune_collapse ,
259 [ " prune and no empty " ] = p_prune_noempty ,
260 [ " prune and to space " ] = p_prune_intospace ,
261 [ " retain " ] = p_retain_normal ,
262 [ " retain and collapse " ] = p_retain_collapse ,
263 [ " retain and no empty " ] = p_retain_noempty ,
264 [ " collapse " ] = patterns . collapser ,
265}
266
267setmetatable ( striplinepatterns , { __index = function ( t , k ) return p_prune_collapse end } )
268
269strings . striplinepatterns = striplinepatterns
270
271function strings . striplines ( str , how )
272 return str and lpegmatch ( striplinepatterns [ how ] , str ) or str
273end
274
275function strings . collapse ( str )
276 return str and lpegmatch ( p_prune_intospace , str ) or str
277end
278
279
280
281strings . striplong = strings . striplines
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312function strings . nice ( str )
313 str = gsub ( str , " [:%-+_]+ " , " " )
314 return str
315end
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377local n = 0
378
379
380
381
382
383
384
385local sequenced = table . sequenced
386
387function string . autodouble ( s , sep )
388 if s = = nil then
389 return ' "" '
390 end
391 local t = type ( s )
392 if t = = " number " then
393 return tostring ( s )
394 end
395 if t = = " table " then
396 return ( ' " ' . . sequenced ( s , sep or " , " ) . . ' " ' )
397 end
398 return ( ' " ' . . tostring ( s ) . . ' " ' )
399end
400
401function string . autosingle ( s , sep )
402 if s = = nil then
403 return " '' "
404 end
405 local t = type ( s )
406 if t = = " number " then
407 return tostring ( s )
408 end
409 if t = = " table " then
410 return ( " ' " . . sequenced ( s , sep or " , " ) . . " ' " )
411 end
412 return ( " ' " . . tostring ( s ) . . " ' " )
413end
414
415local tracedchars = { [ 0 ] =
416
417 " [null] " , " [soh] " , " [stx] " , " [etx] " , " [eot] " , " [enq] " , " [ack] " , " [bel] " ,
418 " [bs] " , " [ht] " , " [lf] " , " [vt] " , " [ff] " , " [cr] " , " [so] " , " [si] " ,
419 " [dle] " , " [dc1] " , " [dc2] " , " [dc3] " , " [dc4] " , " [nak] " , " [syn] " , " [etb] " ,
420 " [can] " , " [em] " , " [sub] " , " [esc] " , " [fs] " , " [gs] " , " [rs] " , " [us] " ,
421
422 " [space] " ,
423}
424
425string . tracedchars = tracedchars
426strings . tracers = tracedchars
427
428function string . tracedchar ( b )
429
430 if type ( b ) = = " number " then
431 return tracedchars [ b ] or ( utfchar ( b ) . . " (U+ " . . format ( " %05X " , b ) . . " ) " )
432 else
433 local c = utfbyte ( b )
434 return tracedchars [ c ] or ( b . . " (U+ " . . ( c and format ( " %05X " , c ) or " ????? " ) . . " ) " )
435 end
436end
437
438function number . signed ( i )
439 if i > 0 then
440 return " + " , i
441 else
442 return " - " , - i
443 end
444end
445
446
447
448local two = digit * digit
449local three = two * digit
450local prefix = ( Carg ( 1 ) * three ) ^ 1
451
452local splitter = Cs (
453 ( ( ( 1 - ( three ^ 1 * period ) ) ^ 1 + C ( three ) ) * prefix + C ( ( 1 - period ) ^ 1 ) )
454 * ( anything / " " * Carg ( 2 ) ) * C ( 2 )
455)
456
457local splitter3 = Cs (
458 three * prefix * endofstring +
459 two * prefix * endofstring +
460 digit * prefix * endofstring +
461 three +
462 two +
463 digit
464)
465
466patterns . formattednumber = splitter
467
468function number . formatted ( n , sep1 , sep2 )
469 if sep1 = = false then
470 if type ( n ) = = " number " then
471 n = tostring ( n )
472 end
473 return lpegmatch ( splitter3 , n , 1 , sep2 or " . " )
474 else
475 if type ( n ) = = " number " then
476 n = format ( " %0.2f " , n )
477 end
478 if sep1 = = true then
479 return lpegmatch ( splitter , n , 1 , " . " , " , " )
480 elseif sep1 = = " . " then
481 return lpegmatch ( splitter , n , 1 , sep1 , sep2 or " , " )
482 elseif sep1 = = " , " then
483 return lpegmatch ( splitter , n , 1 , sep1 , sep2 or " . " )
484 else
485 return lpegmatch ( splitter , n , 1 , sep1 or " , " , sep2 or " . " )
486 end
487 end
488end
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509local p = Cs (
510 P ( " - " ) ^ 0
511 * ( P ( " 0 " ) ^ 1 / " " ) ^ 0
512 * ( 1 - period ) ^ 0
513 * ( period * P ( " 0 " ) ^ 1 * endofstring / " " + period ^ 0 )
514 * P ( 1 - P ( " 0 " ) ^ 1 * endofstring ) ^ 0
515 )
516
517function number . compactfloat ( n , fmt )
518 if n = = 0 then
519 return " 0 "
520 elseif n = = 1 then
521 return " 1 "
522 end
523 n = lpegmatch ( p , format ( fmt or " %0.3f " , n ) )
524 if n = = " . " or n = = " " or n = = " - " then
525 return " 0 "
526 end
527 return n
528end
529
530local zero = P ( " 0 " ) ^ 1 / " "
531local plus = P ( " + " ) / " "
532local minus = P ( " - " )
533local separator = period
534local trailing = zero ^ 1 * # S ( " eE " )
535local exponent = ( S ( " eE " ) * ( plus + Cs ( ( minus * zero ^ 0 * endofstring ) / " " ) + minus ) * zero ^ 0 * ( endofstring * Cc ( " 0 " ) + anything ^ 1 ) )
536local pattern_a = Cs ( minus ^ 0 * digit ^ 1 * ( separator / " " * trailing + separator * ( trailing + digit ) ^ 0 ) * exponent )
537local pattern_b = Cs ( ( exponent + anything ) ^ 0 )
538
539function number . sparseexponent ( f , n )
540 if not n then
541 n = f
542 f = " %e "
543 end
544 local tn = type ( n )
545 if tn = = " string " then
546 local m = tonumber ( n )
547 if m then
548 return lpegmatch ( ( f = = " %e " or f = = " %E " ) and pattern_a or pattern_b , format ( f , m ) )
549 end
550 elseif tn = = " number " then
551 return lpegmatch ( ( f = = " %e " or f = = " %E " ) and pattern_a or pattern_b , format ( f , n ) )
552 end
553 return tostring ( n )
554end
555
556local hf = { }
557local hs = { }
558
559setmetatable ( hf , { __index = function ( t , k )
560 local v = " %. " . . k . . " f "
561 t [ k ] = v
562 return v
563end } )
564
565setmetatable ( hs , { __index = function ( t , k )
566 local v = " % " . . k . . " s "
567 t [ k ] = v
568 return v
569end } )
570
571function number . formattedfloat ( n , b , a )
572 local s = format ( hf [ a ] , n )
573 local l = ( b or 0 ) + ( a or 0 ) + 1
574 if # s < l then
575 return format ( hs [ l ] , s )
576 else
577 return s
578 end
579end
580
581local template = [[
582%s
583%s
584return function(%s) return %s end
585 ]]
586
587
588
589local pattern = Cs ( Cc ( ' " ' ) * (
590 ( 1 - S ( ' "\\\n\r ' ) ) ^ 1
591 + P ( ' " ' ) / ' \\" '
592 + P ( ' \\ ' ) / ' \\\\ '
593 + P ( ' \n ' ) / ' \\n '
594 + P ( ' \r ' ) / ' \\r '
595) ^ 0 * Cc ( ' " ' ) )
596
597patterns . escapedquotes = pattern
598
599function string . escapedquotes ( s )
600 return lpegmatch ( pattern , s )
601end
602
603
604
605
606
607local preamble = " "
608
609local environment = {
610 global = global or _G ,
611 lpeg = lpeg ,
612 type = type ,
613 tostring = tostring ,
614 tonumber = tonumber ,
615 format = string . format ,
616 concat = table . concat ,
617 signed = number . signed ,
618 points = number . points ,
619 basepoints = number . basepoints ,
620 utfchar = utf . char ,
621 utfbyte = utf . byte ,
622 lpegmatch = lpeg . match ,
623 nspaces = string . nspaces ,
624 utfpadding = string . utfpadding ,
625 tracedchar = string . tracedchar ,
626 autosingle = string . autosingle ,
627 autodouble = string . autodouble ,
628 sequenced = table . sequenced ,
629 formattednumber = number . formatted ,
630 sparseexponent = number . sparseexponent ,
631 formattedfloat = number . formattedfloat ,
632 stripzero = patterns . stripzero ,
633 stripzeros = patterns . stripzeros ,
634 escapedquotes = string . escapedquotes ,
635
636 FORMAT = string . f6 ,
637}
638
639
640
641local arguments = { " a1 " }
642
643setmetatable ( arguments , { __index =
644 function ( t , k )
645 local v = t [ k -1 ] . . " ,a " . . k
646 t [ k ] = v
647 return v
648 end
649} )
650
651local prefix_any = C ( ( sign + space + period + digit ) ^ 0 )
652local prefix_sub = ( C ( ( sign + digit ) ^ 0 ) + Cc ( 0 ) )
653 * period
654 * ( C ( ( sign + digit ) ^ 0 ) + Cc ( 0 ) )
655local prefix_tab = P ( " { " ) * C ( ( 1 - P ( " } " ) ) ^ 0 ) * P ( " } " ) + C ( ( 1 - R ( " az " , " AZ " , " 09 " , " %% " ) ) ^ 0 )
656
657
658
659
660
661local format_s = function ( f )
662 n = n + 1
663 if f and f ~ = " " then
664 return format ( " format('%%%ss',a%s) " , f , n )
665 else
666 return format ( " (a%s or '') " , n )
667 end
668end
669
670local format_S = function ( f )
671 n = n + 1
672 if f and f ~ = " " then
673 return format ( " format('%%%ss',tostring(a%s)) " , f , n )
674 else
675 return format ( " tostring(a%s) " , n )
676 end
677end
678
679local format_right = function ( f )
680 n = n + 1
681 f = tonumber ( f )
682 if not f or f = = 0 then
683 return format ( " (a%s or '') " , n )
684 elseif f > 0 then
685 return format ( " utfpadding(a%s,%i)..a%s " , n , f , n )
686 else
687 return format ( " a%s..utfpadding(a%s,%i) " , n , n , f )
688 end
689end
690
691local format_left = function ( f )
692 n = n + 1
693 f = tonumber ( f )
694 if not f or f = = 0 then
695 return format ( " (a%s or '') " , n )
696 end
697 if f < 0 then
698 return format ( " utfpadding(a%s,%i)..a%s " , n , - f , n )
699 else
700 return format ( " a%s..utfpadding(a%s,%i) " , n , n , - f )
701 end
702end
703
704local format_q = JITSUPPORTED and function ( )
705 n = n + 1
706
707
708 return format ( " (a%s ~= nil and format('%%q',tostring(a%s)) or '') " , n , n )
709
710end or function ( )
711 n = n + 1
712 return format ( " (a%s ~= nil and format('%%q',a%s) or '') " , n , n )
713end
714
715
716local format_Q = function ( )
717 n = n + 1
718
719 return format ( " escapedquotes(tostring(a%s)) " , n )
720end
721
722local format_i = function ( f )
723 n = n + 1
724 if f and f ~ = " " then
725 return format ( " format('%%%si',a%s) " , f , n )
726 else
727 return format ( " format('%%i',a%s) " , n )
728 end
729end
730
731local format_d = format_i
732
733local format_I = function ( f )
734 n = n + 1
735 return format ( " format('%%s%%%si',signed(a%s)) " , f , n )
736end
737
738local format_f = function ( f )
739 n = n + 1
740 return format ( " format('%%%sf',a%s) " , f , n )
741end
742
743
744
745
746
747
748
749
750
751
752local format_F = function ( f )
753 n = n + 1
754 if not f or f = = " " then
755 return format ( " (((a%s > -0.0000000005 and a%s < 0.0000000005) and '0') or format((a%s %% 1 == 0) and '%%i' or '%%.9f',a%s)) " , n , n , n , n )
756 else
757 return format ( " format((a%s %% 1 == 0) and '%%i' or '%%%sf',a%s) " , n , f , n )
758 end
759end
760
761
762
763
764
765
766
767
768
769
770
771
772local format_k = function ( b , a )
773 n = n + 1
774 return format ( " formattedfloat(a%s,%s,%s) " , n , b or 0 , a or 0 )
775end
776
777local format_g = function ( f )
778 n = n + 1
779 return format ( " format('%%%sg',a%s) " , f , n )
780end
781
782local format_G = function ( f )
783 n = n + 1
784 return format ( " format('%%%sG',a%s) " , f , n )
785end
786
787local format_e = function ( f )
788 n = n + 1
789 return format ( " format('%%%se',a%s) " , f , n )
790end
791
792local format_E = function ( f )
793 n = n + 1
794 return format ( " format('%%%sE',a%s) " , f , n )
795end
796
797local format_j = function ( f )
798 n = n + 1
799 return format ( " sparseexponent('%%%se',a%s) " , f , n )
800end
801
802local format_J = function ( f )
803 n = n + 1
804 return format ( " sparseexponent('%%%sE',a%s) " , f , n )
805end
806
807local format_x = function ( f )
808 n = n + 1
809 return format ( " format('%%%sx',a%s) " , f , n )
810end
811
812local format_X = function ( f )
813 n = n + 1
814 return format ( " format('%%%sX',a%s) " , f , n )
815end
816
817local format_o = function ( f )
818 n = n + 1
819 return format ( " format('%%%so',a%s) " , f , n )
820end
821
822local format_c = function ( )
823 n = n + 1
824 return format ( " utfchar(a%s) " , n )
825end
826
827local format_C = function ( )
828 n = n + 1
829 return format ( " tracedchar(a%s) " , n )
830end
831
832local format_r = function ( f )
833 n = n + 1
834 return format ( " format('%%%s.0f',a%s) " , f , n )
835end
836
837local format_h = function ( f )
838 n = n + 1
839 if f = = " - " then
840 f = sub ( f , 2 )
841 return format ( " format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s)) " , f = = " " and " 05 " or f , n , n , n )
842 else
843 return format ( " format('0x%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s)) " , f = = " " and " 05 " or f , n , n , n )
844 end
845end
846
847local format_H = function ( f )
848 n = n + 1
849 if f = = " - " then
850 f = sub ( f , 2 )
851 return format ( " format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s)) " , f = = " " and " 05 " or f , n , n , n )
852 else
853 return format ( " format('0x%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s)) " , f = = " " and " 05 " or f , n , n , n )
854 end
855end
856
857local format_u = function ( f )
858 n = n + 1
859 if f = = " - " then
860 f = sub ( f , 2 )
861 return format ( " format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s)) " , f = = " " and " 05 " or f , n , n , n )
862 else
863 return format ( " format('u+%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s)) " , f = = " " and " 05 " or f , n , n , n )
864 end
865end
866
867local format_U = function ( f )
868 n = n + 1
869 if f = = " - " then
870 f = sub ( f , 2 )
871 return format ( " format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s)) " , f = = " " and " 05 " or f , n , n , n )
872 else
873 return format ( " format('U+%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s)) " , f = = " " and " 05 " or f , n , n , n )
874 end
875end
876
877local format_p = function ( )
878 n = n + 1
879 return format ( " points(a%s) " , n )
880end
881
882local format_b = function ( )
883 n = n + 1
884 return format ( " basepoints(a%s) " , n )
885end
886
887local format_t = function ( f )
888 n = n + 1
889 if f and f ~ = " " then
890 return format ( " concat(a%s,%q) " , n , f )
891 else
892 return format ( " concat(a%s) " , n )
893 end
894end
895
896local format_T = function ( f )
897 n = n + 1
898 if f and f ~ = " " then
899 return format ( " sequenced(a%s,%q) " , n , f )
900 else
901 return format ( " sequenced(a%s) " , n )
902 end
903end
904
905local format_l = function ( )
906 n = n + 1
907 return format ( " (a%s and 'true' or 'false') " , n )
908end
909
910local format_L = function ( )
911 n = n + 1
912 return format ( " (a%s and 'TRUE' or 'FALSE') " , n )
913end
914
915local format_n = function ( )
916 n = n + 1
917 return format ( " ((a%s %% 1 == 0) and format('%%i',a%s) or tostring(a%s)) " , n , n , n )
918end
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945local format_N if environment . FORMAT then
946
947 format_N = function ( f )
948 n = n + 1
949 if not f or f = = " " then
950 return format ( " FORMAT(a%s,'%%.9f') " , n )
951 elseif f = = " .6 " or f = = " 0.6 " then
952 return format ( " FORMAT(a%s) " , n )
953 else
954 return format ( " FORMAT(a%s,'%%%sf') " , n , f )
955 end
956 end
957
958else
959
960 format_N = function ( f )
961 n = n + 1
962
963 if not f or f = = " " then
964 f = " .9 "
965 end
966 return format ( " (((a%s %% 1 == 0) and format('%%i',a%s)) or lpegmatch(stripzero,format('%%%sf',a%s))) " , n , n , f , n )
967 end
968
969end
970
971local format_a = function ( f )
972 n = n + 1
973 if f and f ~ = " " then
974 return format ( " autosingle(a%s,%q) " , n , f )
975 else
976 return format ( " autosingle(a%s) " , n )
977 end
978end
979
980local format_A = function ( f )
981 n = n + 1
982 if f and f ~ = " " then
983 return format ( " autodouble(a%s,%q) " , n , f )
984 else
985 return format ( " autodouble(a%s) " , n )
986 end
987end
988
989local format_w = function ( f )
990 n = n + 1
991 f = tonumber ( f )
992 if f then
993 return format ( " nspaces[%s+a%s] " , f , n )
994 else
995 return format ( " nspaces[a%s] " , n )
996 end
997end
998
999local format_W = function ( f )
1000 return format ( " nspaces[%s] " , tonumber ( f ) or 0 )
1001end
1002
1003local format_m = function ( f )
1004 n = n + 1
1005 if not f or f = = " " then
1006 f = " , "
1007 end
1008 if f = = " 0 " then
1009 return format ( [[ formattednumber(a%s,false) ]] , n )
1010 else
1011 return format ( [[ formattednumber(a%s,%q,".") ]] , n , f )
1012 end
1013end
1014
1015local format_M = function ( f )
1016 n = n + 1
1017 if not f or f = = " " then
1018 f = " . "
1019 end
1020 if f = = " 0 " then
1021 return format ( [[ formattednumber(a%s,false) ]] , n )
1022 else
1023 return format ( [[ formattednumber(a%s,%q,",") ]] , n , f )
1024 end
1025end
1026
1027
1028
1029local format_z = function ( f )
1030 n = n + ( tonumber ( f ) or 1 )
1031 return " '' "
1032end
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056local format_rest = function ( s )
1057 return format ( " %q " , s )
1058end
1059
1060local format_extension = function ( extensions , f , name )
1061 local extension = extensions [ name ] or " tostring(%s) "
1062 local f = tonumber ( f ) or 1
1063 local w = find ( extension , " %.%.%. " )
1064 if f = = 0 then
1065 if w then
1066 extension = gsub ( extension , " %.%.%. " , " " )
1067 end
1068 return extension
1069 elseif f = = 1 then
1070 if w then
1071 extension = gsub ( extension , " %.%.%. " , " %%s " )
1072 end
1073 n = n + 1
1074 local a = " a " . . n
1075 return format ( extension , a , a )
1076 elseif f < 0 then
1077 if w then
1078
1079 extension = gsub ( extension , " %.%.%. " , " " )
1080 return extension
1081 else
1082 local a = " a " . . ( n + f + 1 )
1083 return format ( extension , a , a )
1084 end
1085 else
1086 if w then
1087 extension = gsub ( extension , " %.%.%. " , rep ( " %%s, " , f -1 ) . . " %%s " )
1088 end
1089
1090
1091 local t = { }
1092 for i = 1 , f do
1093 n = n + 1
1094
1095 t [ i ] = " a " . . n
1096 end
1097 return format ( extension , unpack ( t ) )
1098 end
1099end
1100
1101
1102
1103
1104
1105
1106
1107local builder = Cs { " start " ,
1108 start = (
1109 (
1110 P ( " % " ) / " "
1111 * (
1112 V ( " ! " )
1113 + V ( " s " ) + V ( " q " )
1114 + V ( " i " ) + V ( " d " )
1115 + V ( " f " ) + V ( " F " ) + V ( " g " ) + V ( " G " ) + V ( " e " ) + V ( " E " )
1116 + V ( " x " ) + V ( " X " ) + V ( " o " )
1117
1118 + V ( " c " )
1119 + V ( " C " )
1120 + V ( " S " )
1121 + V ( " Q " )
1122 + V ( " n " )
1123 + V ( " N " )
1124 + V ( " k " )
1125
1126 + V ( " r " )
1127 + V ( " h " ) + V ( " H " ) + V ( " u " ) + V ( " U " )
1128 + V ( " p " ) + V ( " b " )
1129 + V ( " t " ) + V ( " T " )
1130 + V ( " l " ) + V ( " L " )
1131 + V ( " I " )
1132 + V ( " w " )
1133 + V ( " W " )
1134 + V ( " a " )
1135 + V ( " A " )
1136 + V ( " j " ) + V ( " J " )
1137 + V ( " m " ) + V ( " M " )
1138 + V ( " z " )
1139
1140 + V ( " > " )
1141 + V ( " < " )
1142
1143
1144 )
1145 + V ( " * " )
1146 )
1147 * ( endofstring + Carg ( 1 ) )
1148 ) ^ 0 ,
1149
1150 [ " s " ] = ( prefix_any * P ( " s " ) ) / format_s ,
1151 [ " q " ] = ( prefix_any * P ( " q " ) ) / format_q ,
1152 [ " i " ] = ( prefix_any * P ( " i " ) ) / format_i ,
1153 [ " d " ] = ( prefix_any * P ( " d " ) ) / format_d ,
1154 [ " f " ] = ( prefix_any * P ( " f " ) ) / format_f ,
1155 [ " F " ] = ( prefix_any * P ( " F " ) ) / format_F ,
1156 [ " g " ] = ( prefix_any * P ( " g " ) ) / format_g ,
1157 [ " G " ] = ( prefix_any * P ( " G " ) ) / format_G ,
1158 [ " e " ] = ( prefix_any * P ( " e " ) ) / format_e ,
1159 [ " E " ] = ( prefix_any * P ( " E " ) ) / format_E ,
1160 [ " x " ] = ( prefix_any * P ( " x " ) ) / format_x ,
1161 [ " X " ] = ( prefix_any * P ( " X " ) ) / format_X ,
1162 [ " o " ] = ( prefix_any * P ( " o " ) ) / format_o ,
1163
1164 [ " S " ] = ( prefix_any * P ( " S " ) ) / format_S ,
1165 [ " Q " ] = ( prefix_any * P ( " Q " ) ) / format_Q ,
1166 [ " n " ] = ( prefix_any * P ( " n " ) ) / format_n ,
1167 [ " N " ] = ( prefix_any * P ( " N " ) ) / format_N ,
1168 [ " k " ] = ( prefix_sub * P ( " k " ) ) / format_k ,
1169 [ " c " ] = ( prefix_any * P ( " c " ) ) / format_c ,
1170 [ " C " ] = ( prefix_any * P ( " C " ) ) / format_C ,
1171
1172 [ " r " ] = ( prefix_any * P ( " r " ) ) / format_r ,
1173 [ " h " ] = ( prefix_any * P ( " h " ) ) / format_h ,
1174 [ " H " ] = ( prefix_any * P ( " H " ) ) / format_H ,
1175 [ " u " ] = ( prefix_any * P ( " u " ) ) / format_u ,
1176 [ " U " ] = ( prefix_any * P ( " U " ) ) / format_U ,
1177 [ " p " ] = ( prefix_any * P ( " p " ) ) / format_p ,
1178 [ " b " ] = ( prefix_any * P ( " b " ) ) / format_b ,
1179 [ " t " ] = ( prefix_tab * P ( " t " ) ) / format_t ,
1180 [ " T " ] = ( prefix_tab * P ( " T " ) ) / format_T ,
1181 [ " l " ] = ( prefix_any * P ( " l " ) ) / format_l ,
1182 [ " L " ] = ( prefix_any * P ( " L " ) ) / format_L ,
1183 [ " I " ] = ( prefix_any * P ( " I " ) ) / format_I ,
1184
1185 [ " w " ] = ( prefix_any * P ( " w " ) ) / format_w ,
1186 [ " W " ] = ( prefix_any * P ( " W " ) ) / format_W ,
1187
1188 [ " j " ] = ( prefix_any * P ( " j " ) ) / format_j ,
1189 [ " J " ] = ( prefix_any * P ( " J " ) ) / format_J ,
1190
1191 [ " m " ] = ( prefix_any * P ( " m " ) ) / format_m ,
1192 [ " M " ] = ( prefix_any * P ( " M " ) ) / format_M ,
1193
1194 [ " z " ] = ( prefix_any * P ( " z " ) ) / format_z ,
1195
1196
1197 [ " a " ] = ( prefix_any * P ( " a " ) ) / format_a ,
1198 [ " A " ] = ( prefix_any * P ( " A " ) ) / format_A ,
1199
1200 [ " < " ] = ( prefix_any * P ( " < " ) ) / format_left ,
1201 [ " > " ] = ( prefix_any * P ( " > " ) ) / format_right ,
1202
1203 [ " * " ] = Cs ( ( ( 1 - P ( " % " ) ) ^ 1 + P ( " %% " ) / " %% " ) ^ 1 ) / format_rest ,
1204 [ " ? " ] = Cs ( ( ( 1 - P ( " % " ) ) ^ 1 ) ^ 1 ) / format_rest ,
1205
1206 [ " ! " ] = Carg ( 2 ) * prefix_any * P ( " ! " ) * C ( ( 1 - P ( " ! " ) ) ^ 1 ) * P ( " ! " ) / format_extension ,
1207}
1208
1209
1210
1211local xx = setmetatable ( { } , { __index = function ( t , k ) local v = format ( " %02x " , k ) t [ k ] = v return v end } )
1212local XX = setmetatable ( { } , { __index = function ( t , k ) local v = format ( " %02X " , k ) t [ k ] = v return v end } )
1213
1214local preset = {
1215 [ " %02x " ] = function ( n ) return xx [ n ] end ,
1216 [ " %02X " ] = function ( n ) return XX [ n ] end ,
1217}
1218
1219local direct =
1220 P ( " % " ) * ( sign + space + period + digit ) ^ 0 * S ( " sqidfgGeExXo " ) * endofstring
1221 / [[ local format = string.format return function(str) return format("%0",str) end ]]
1222
1223local function make ( t , str )
1224 local f = preset [ str ]
1225 if f then
1226 return f
1227 end
1228 local p = lpegmatch ( direct , str )
1229 if p then
1230
1231 f = loadstripped ( p ) ( )
1232 else
1233 n = 0
1234
1235 p = lpegmatch ( builder , str , 1 , t . _connector_ , t . _extensions_ )
1236 if n > 0 then
1237 p = format ( template , preamble , t . _preamble_ , arguments [ n ] , p )
1238
1239 f = loadstripped ( p , t . _environment_ ) ( )
1240 else
1241 f = function ( ) return str end
1242 end
1243 end
1244 t [ str ] = f
1245 return f
1246end
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284local function use ( t , fmt , ... )
1285 return t [ fmt ] ( ... )
1286end
1287
1288strings . formatters = { }
1289
1290
1291
1292
1293
1294
1295
1296function strings . formatters . new ( noconcat )
1297 local e = { }
1298 for k , v in next , environment do
1299 e [ k ] = v
1300 end
1301 local t = {
1302 _type_ = " formatter " ,
1303 _connector_ = noconcat and " , " or " .. " ,
1304 _extensions_ = { } ,
1305 _preamble_ = " " ,
1306 _environment_ = e ,
1307 }
1308 setmetatable ( t , { __index = make , __call = use } )
1309 return t
1310end
1311
1312local formatters = strings . formatters . new ( )
1313
1314string . formatters = formatters
1315string . formatter = function ( str , ... ) return formatters [ str ] ( ... ) end
1316
1317local function add ( t , name , template , preamble )
1318 if type ( t ) = = " table " and t . _type_ = = " formatter " then
1319 t . _extensions_ [ name ] = template or " %s "
1320 if type ( preamble ) = = " string " then
1321 t . _preamble_ = preamble . . " \n " . . t . _preamble_
1322 elseif type ( preamble ) = = " table " then
1323 for k , v in next , preamble do
1324 t . _environment_ [ k ] = v
1325 end
1326 end
1327 end
1328end
1329
1330strings . formatters . add = add
1331
1332
1333
1334patterns . xmlescape = Cs ( ( P ( " < " ) / " < " + P ( " > " ) / " > " + P ( " & " ) / " & " + P ( ' " ' ) / " " " + anything ) ^ 0 )
1335patterns . texescape = Cs ( ( C ( S ( " #$%\\{} " ) ) / " \\%1 " + anything ) ^ 0 )
1336patterns . luaescape = Cs ( ( ( 1 - S ( ' "\n ' ) ) ^ 1 + P ( ' " ' ) / ' \\" ' + P ( ' \n ' ) / ' \\n" ' ) ^ 0 )
1337patterns . luaquoted = Cs ( Cc ( ' " ' ) * ( ( 1 - S ( ' "\n ' ) ) ^ 1 + P ( ' " ' ) / ' \\" ' + P ( ' \n ' ) / ' \\n" ' ) ^ 0 * Cc ( ' " ' ) )
1338
1339
1340
1341
1342add ( formatters , " xml " , [[ lpegmatch(xmlescape,%s) ]] , { xmlescape = patterns . xmlescape } )
1343add ( formatters , " tex " , [[ lpegmatch(texescape,%s) ]] , { texescape = patterns . texescape } )
1344add ( formatters , " lua " , [[ lpegmatch(luaescape,%s) ]] , { luaescape = patterns . luaescape } )
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379local dquote = patterns . dquote
1380local equote = patterns . escaped + dquote / ' \\" ' + 1
1381local cquote = Cc ( ' " ' )
1382
1383local pattern =
1384 Cs ( dquote * ( equote - P ( -2 ) ) ^ 0 * dquote )
1385 + Cs ( cquote * ( equote - space ) ^ 0 * space * equote ^ 0 * cquote )
1386
1387function string . optionalquoted ( str )
1388 return lpegmatch ( pattern , str ) or str
1389end
1390
1391local pattern = Cs ( ( newline / ( os . newline or " \r " ) + 1 ) ^ 0 )
1392
1393function string . replacenewlines ( str )
1394 return lpegmatch ( pattern , str )
1395end
1396
1397
1398
1399function strings . newcollector ( )
1400 local result , r = { } , 0
1401 return
1402 function ( fmt , str , ... )
1403 r = r + 1
1404 result [ r ] = str = = nil and fmt or formatters [ fmt ] ( str , ... )
1405 end ,
1406 function ( connector )
1407 if result then
1408 local str = concat ( result , connector )
1409 result , r = { } , 0
1410 return str
1411 end
1412 end
1413end
1414
1415
1416
1417local f_16_16 = formatters [ " %0.5N " ]
1418
1419function number . to16dot16 ( n )
1420 return f_16_16 ( n / 65536 . 0 )
1421end
1422
1423
1424
1425if not string . explode then
1426
1427 local tsplitat = lpeg . tsplitat
1428
1429 local p_utf = patterns . utf8character
1430 local p_check = C ( p_utf ) * ( P ( " + " ) * Cc ( true ) ) ^ 0
1431 local p_split = Ct ( C ( p_utf ) ^ 0 )
1432 local p_space = Ct ( ( C ( 1 - P ( " " ) ^ 1 ) + P ( " " ) ^ 1 ) ^ 0 )
1433
1434 function string . explode ( str , symbol )
1435 if symbol = = " " then
1436 return lpegmatch ( p_split , str )
1437 elseif symbol then
1438 local a , b = lpegmatch ( p_check , symbol )
1439 if b then
1440 return lpegmatch ( tsplitat ( P ( a ) ^ 1 ) , str )
1441 else
1442 return lpegmatch ( tsplitat ( a ) , str )
1443 end
1444 else
1445 return lpegmatch ( p_space , str )
1446 end
1447 end
1448
1449end
1450 |