plurimath 0.10.7 → 0.10.9.pre.1

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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +3 -2
  3. data/README.adoc +343 -44
  4. data/lib/plurimath/asciimath/parse.rb +6 -2
  5. data/lib/plurimath/configuration.rb +17 -0
  6. data/lib/plurimath/deprecation.rb +81 -0
  7. data/lib/plurimath/errors/configuration_error.rb +27 -0
  8. data/lib/plurimath/errors/deprecation_error.rb +33 -0
  9. data/lib/plurimath/errors/error.rb +6 -0
  10. data/lib/plurimath/errors/formatter/unsupported_base.rb +1 -1
  11. data/lib/plurimath/errors/formatter/unsupported_locale.rb +18 -0
  12. data/lib/plurimath/errors/omml/unsupported_node_error.rb +1 -1
  13. data/lib/plurimath/errors/parse_error.rb +1 -1
  14. data/lib/plurimath/errors/parse_option_error.rb +34 -0
  15. data/lib/plurimath/formatter/numbers/base.rb +18 -8
  16. data/lib/plurimath/formatter/numbers/base_notation.rb +67 -0
  17. data/lib/plurimath/formatter/numbers/digit_sequence.rb +96 -0
  18. data/lib/plurimath/formatter/numbers/format_options.rb +141 -0
  19. data/lib/plurimath/formatter/numbers/fraction.rb +50 -93
  20. data/lib/plurimath/formatter/numbers/integer.rb +30 -6
  21. data/lib/plurimath/formatter/numbers/notation_renderer.rb +128 -0
  22. data/lib/plurimath/formatter/numbers/number_renderer.rb +66 -0
  23. data/lib/plurimath/formatter/numbers/parts.rb +69 -0
  24. data/lib/plurimath/formatter/numbers/parts_renderer.rb +30 -0
  25. data/lib/plurimath/formatter/numbers/precision_resolver.rb +54 -0
  26. data/lib/plurimath/formatter/numbers/sign_renderer.rb +28 -0
  27. data/lib/plurimath/formatter/numbers/significant.rb +77 -103
  28. data/lib/plurimath/formatter/numbers/source.rb +120 -0
  29. data/lib/plurimath/formatter/numbers/symbol_resolver.rb +55 -0
  30. data/lib/plurimath/formatter/numbers.rb +11 -0
  31. data/lib/plurimath/formatter/standard.rb +32 -42
  32. data/lib/plurimath/formatter/supported_locales.rb +27 -0
  33. data/lib/plurimath/formatter.rb +1 -2
  34. data/lib/plurimath/html/constants.rb +2 -0
  35. data/lib/plurimath/html/parse.rb +77 -14
  36. data/lib/plurimath/html/parser.rb +15 -3
  37. data/lib/plurimath/html/transform.rb +193 -91
  38. data/lib/plurimath/html/transform_utility.rb +61 -0
  39. data/lib/plurimath/html.rb +1 -0
  40. data/lib/plurimath/latex/parse.rb +7 -1
  41. data/lib/plurimath/latex/transform.rb +5 -5
  42. data/lib/plurimath/math/function/lim.rb +6 -0
  43. data/lib/plurimath/math/number.rb +8 -2
  44. data/lib/plurimath/math/symbols/cdot.rb +1 -1
  45. data/lib/plurimath/math/symbols/exclam.rb +1 -1
  46. data/lib/plurimath/math/symbols/minus.rb +1 -1
  47. data/lib/plurimath/math/symbols/percent.rb +1 -1
  48. data/lib/plurimath/math/symbols/pi.rb +1 -1
  49. data/lib/plurimath/math/symbols/slash.rb +1 -1
  50. data/lib/plurimath/math.rb +56 -8
  51. data/lib/plurimath/number_formatter.rb +57 -27
  52. data/lib/plurimath/unicode_math/parse.rb +7 -1
  53. data/lib/plurimath/unicode_math/transform.rb +2 -2
  54. data/lib/plurimath/version.rb +1 -1
  55. data/lib/plurimath.rb +23 -1
  56. metadata +21 -4
  57. data/lib/plurimath/formatter/number_formatter.rb +0 -115
  58. data/lib/plurimath/formatter/numeric_formatter.rb +0 -187
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14c446bc1c84d523b37daea2d902eb69ecc7a5ee9b586dc425da9780b8d84560
4
- data.tar.gz: 69f9e89f24bb17ebb5d9b2d8b9f5f91faeb7469d0b838b8e20e63b7fadef08c7
3
+ metadata.gz: e399a104da75213be9f312c84c413650fd5a52c15f159ffa53587ea7edb8da50
4
+ data.tar.gz: d2aeea44e7ebaf4a761337b534a9720a8da261b5e3f552c1ddc8b50edaff7599
5
5
  SHA512:
6
- metadata.gz: b1f55afb6b629f26a4766bbc5839cce6814b5d47c743c91c7c232639d13ea3411c5b73531976b094b5de57157ef12f6df0ce1586f33c1a529916c9af4c275ec1
7
- data.tar.gz: 8c07b342deb4e97f1993a5825ea5d53cdb1e8f96ccd65e37efdee90d1b4b960773bbcb41737df348ca1ca2c055e222f30837c173d234334fc368d0e30375fd21
6
+ metadata.gz: 676a1e8b22a782617673e665b0fa53bdb5fe6e6c348cf893517dcb648984ad58418d43a6cca7a6e7d5bbe1bdaae66339a0995aeff096292fee0f51827dc2bd1f
7
+ data.tar.gz: 8e2e2c44156b845ca0f0e9aa3354586c89cb3d057ef8dd9b7072e4a23232b785a217ef068cc8beb42d4f00d6e376a2ef4022065dfeaa53c6c573b64a9047da2a
@@ -10,8 +10,8 @@ on:
10
10
  Next release version. Possible values: x.y.z, major, minor, patch or pre|rc|etc
11
11
  required: true
12
12
  default: 'skip'
13
- push:
14
- tags: [ v* ]
13
+ repository_dispatch:
14
+ types: [ do-release ]
15
15
 
16
16
  jobs:
17
17
  release:
@@ -23,3 +23,4 @@ jobs:
23
23
  next_version: ${{ github.event.inputs.next_version }}
24
24
  secrets:
25
25
  rubygems-api-key: ${{ secrets.PLURIMATH_CI_RUBYGEMS_API_KEY }}
26
+ pat_token: ${{ secrets.PLURIMATH_CI_PAT_TOKEN }}
data/README.adoc CHANGED
@@ -160,6 +160,72 @@ asciimath = "sin(1)"
160
160
  formula = Plurimath::Math.parse(asciimath, :asciimath)
161
161
  ----
162
162
 
163
+ ==== Localized decimal input
164
+
165
+ AsciiMath, LaTeX, HTML, and UnicodeMath input can use a locale decimal marker
166
+ from `Plurimath::Formatter::SupportedLocales::LOCALES` when parsing numbers.
167
+ Pass `locale:` to `Plurimath::Math.parse` for one parse, or use
168
+ `Plurimath.configure` for persistent defaults.
169
+
170
+ For AsciiMath, LaTeX, and HTML, the configured marker is the decimal marker.
171
+ When a locale uses a non-full-stop marker, `1.2` is parsed as `1`, `.`, and
172
+ `2`, not as one decimal number.
173
+
174
+ [source,ruby]
175
+ ----
176
+ formula = Plurimath::Math.parse("1,2", :asciimath, locale: :fr)
177
+ ----
178
+
179
+ When the locale decimal marker is a comma, unspaced digit-comma-digit input is
180
+ read as a decimal number.
181
+
182
+ [source,asciimath]
183
+ ----
184
+ 1,2 # decimal number
185
+ 1 , 2 # comma separator
186
+ ----
187
+
188
+ AsciiMath uses commas for expression and table separators, so separator commas
189
+ between digit tokens should be written with spaces around them when the
190
+ configured decimal marker is a comma.
191
+
192
+ HTML can use the same configured decimal marker inside plain text or tags.
193
+
194
+ [source,html]
195
+ ----
196
+ 1,2
197
+ <span>1,2</span>
198
+ ----
199
+
200
+ Locales are not limited to full stop and comma markers. For example, Arabic and
201
+ Persian locales use the Arabic decimal separator.
202
+
203
+ [source,ruby]
204
+ ----
205
+ formula = Plurimath::Math.parse("1٫2", :unicode, locale: :ar)
206
+ ----
207
+
208
+ LaTeX ordinary spaces are removed during parser preprocessing, so `1, 2` is
209
+ equivalent to `1,2` for this parser. With a comma decimal marker, only
210
+ digit-comma-digit is parsed as a decimal number; a leading comma remains a comma
211
+ symbol followed by a number. To write a comma separator between two digits,
212
+ wrap the comma or the following value in a group, such as `1{,}2` or `1,{}2`.
213
+
214
+ [source,latex]
215
+ ----
216
+ 1,2 % decimal number
217
+ 1{,}2 % comma separator
218
+ 1,{}2 % comma separator
219
+ ----
220
+
221
+ UnicodeMath is different: it keeps its existing full stop and comma decimal
222
+ grammar, including leading markers such as `.2` and `,2`, and also accepts the
223
+ configured locale marker.
224
+
225
+ Localized decimal parsing preserves the parsed number text, such as `1,2` or
226
+ `1٫2`. Number formatter input remains canonical decimal input; locale decimal
227
+ and grouping symbols are applied when formatting output.
228
+
163
229
  ==== MathML Formula example
164
230
 
165
231
  [source,ruby]
@@ -403,6 +469,11 @@ priority, ordered from highest to lowest precedence:
403
469
  . The `localizer_symbols` hash in the creation of a `Plurimath::NumberFormatter`
404
470
  . The **default configuration** of the locale of the `Plurimath::NumberFormatter`
405
471
 
472
+ `precision` is resolved separately: the `precision:` keyword passed to
473
+ `localized_number`, which defaults to the formatter instance's `precision`
474
+ value, takes precedence over any `precision` provided through the formatter
475
+ symbols or `format` hash.
476
+
406
477
 
407
478
  [example]
408
479
  .Formatting a number to group every 2 digits
@@ -488,6 +559,9 @@ formatter = Plurimath::NumberFormatter.new(:en)
488
559
  The `Plurimath::NumberFormatter` object can be configured using either the
489
560
  `localize_number` or `localizer_symbols` options.
490
561
 
562
+ `Plurimath::Formatter::Standard` exposes the same configuration through the
563
+ `string_format` and `options` initializer arguments.
564
+
491
565
 
492
566
  [[localizer_symbols]]
493
567
  ===== Via "format options" using `localizer_symbols`
@@ -499,14 +573,17 @@ This Hash object is called the "format options Hash".
499
573
 
500
574
  Available options are explained below.
501
575
 
502
- NOTE: Each option takes an input of a certain specified type (`String` or
503
- `Numeric`). Using an input type other than the specified type will result in
504
- errors or incorrect output.
576
+ NOTE: Each option takes an input of a certain specified type. Using an input
577
+ type other than the specified type will result in errors or incorrect output.
505
578
 
506
579
  The values passed to `localizer_symbols` persist as long as the initialized
507
580
  `NumberFormatter` instance is accessible. It is therefore useful in scenarios
508
581
  when configuration will be static or changes are not required very often.
509
582
 
583
+ The same option keys can be passed through `localizer_symbols`, through the
584
+ per-call `format:` hash, or through `Plurimath::Formatter::Standard`'s
585
+ `options:` argument.
586
+
510
587
 
511
588
  `decimal`:: (`String` value)
512
589
  Symbol to use for the decimal point. Accepts a character.
@@ -566,6 +643,60 @@ Accepts an integer value. (default is 3 in most locales.)
566
643
  "32232.232" => "3 22 32.232"
567
644
  ====
568
645
 
646
+ `number_sign`:: (`Symbol` or `String` value)
647
+ Controls whether positive numbers are rendered with an explicit sign. Use
648
+ `:plus` or `"plus"` to add a leading `+` to positive values. Negative values
649
+ keep their normal `-` sign. The sign is applied before base prefixes and before
650
+ notation rendering.
651
+ +
652
+ .Showing a positive number sign
653
+ [example]
654
+ ====
655
+ When `base` is used, the input string is still the source decimal value; `base`
656
+ controls the rendered output base.
657
+
658
+ "14236.39239" with `number_sign: :plus` => "+14,236.39239"
659
+ "10" with `base: 2, number_sign: :plus, group_digits: 2, group: ","` => "+0b10,10"
660
+ ====
661
+
662
+ `padding`:: (`String` value)
663
+ Character to use when padding the integer portion. Defaults to `0`.
664
+ +
665
+ .Using spaces as integer padding
666
+ [example]
667
+ ====
668
+ "32" with `padding_digits: 6, padding: " ", group: ""` => " 32"
669
+ ====
670
+
671
+ `padding_digits`:: (`Numeric` value)
672
+ Minimum number of integer digits to render. Missing integer digits are inserted
673
+ before grouping using the configured `padding` character. Do not use it together
674
+ with `padding_group_digits`.
675
+ +
676
+ Padding is applied before integer grouping, so padding characters are grouped
677
+ with the rest of the integer digits.
678
+ +
679
+ .Padding to a fixed integer width
680
+ [example]
681
+ ====
682
+ "32" with `padding_digits: 6, group: ""` => "000032"
683
+ "32123" with `padding_digits: 6, group: ""` => "032123"
684
+ "32" with `padding_digits: 6, group_digits: 3, group: " "` => "000 032"
685
+ ====
686
+
687
+ `padding_group_digits`:: (`Numeric` value)
688
+ Pads the integer portion to the lowest multiple of the configured digit count.
689
+ Missing integer digits are inserted before grouping using the configured
690
+ `padding` character. Do not use it together with `padding_digits`.
691
+ +
692
+ .Padding to an integer width multiple
693
+ [example]
694
+ ====
695
+ "32" with `padding_group_digits: 4, group: ""` => "0032"
696
+ "32123" with `padding_group_digits: 4, group: ""` => "00032123"
697
+ ====
698
+ ====
699
+
569
700
  `base`:: (`Numeric` value)
570
701
  Sets the numeric base (radix) used to render both the integer and fractional parts of the number.
571
702
  Supported values are 2 (binary), 8 (octal), 10 (decimal, default) and 16 (hexadecimal).
@@ -593,30 +724,39 @@ When omitted, standard prefixes are used (`0b` for base 2, `0o` for base 8, `0x`
593
724
  "255" with `base: 16, base_prefix: ""` => "ff"
594
725
  ====
595
726
 
596
- `base_postfix`:: (`String` value)
597
- If present, a postfix is appended to the converted number instead of using any base prefix.
727
+ `base_postfix`:: (`String` or `nil` value)
728
+ If present without `base_prefix`, a postfix is appended to the converted number
729
+ instead of using the default base prefix. When `base_prefix` is also provided,
730
+ the prefix and postfix are both rendered.
598
731
  This is applied after digit grouping.
599
- `nil` or an empty string can be used to omit the postfix.
732
+ Because the formatter checks whether the `base_postfix` key is present, setting
733
+ it to `nil` or an empty string omits both the postfix and the default base
734
+ prefix.
600
735
  +
601
736
  .Using postfix notation
602
737
  [example]
603
738
  ====
604
739
  "255" with `base: 16, base_postfix: "_16"` => "ff_16"
740
+ "255" with `base: 16, base_prefix: "16#", base_postfix: "_16"` => "16#ff_16"
605
741
  "255" with `base: 16, base_postfix: nil` => "ff"
606
742
  "255" with `base: 16, base_postfix: ""` => "ff"
607
743
  ====
608
744
 
609
- `hex_capital`:: (`Boolean` value)
610
- When `true` and `base: 16`, all characters in the range `a`-`f` in the converted output
611
- are rendered using uppercase letters. This includes both hexadecimal digits and any
612
- separators (such as `decimal`, `group`, `fraction_group`) that happen to be letters
613
- in the `a`-`f` range. The `base_prefix` and `base_postfix` values are never modified.
614
- It has no effect for other bases.
745
+ `hex_capital`:: (`Boolean` or `:numbers_only` value)
746
+ Controls uppercase rendering for hexadecimal output. When `true` and `base: 16`,
747
+ all characters in the range `a`-`f` in the converted output are rendered using
748
+ uppercase letters. This includes both hexadecimal digits and any separators
749
+ (such as `decimal`, `group`, `fraction_group`) that happen to be letters in the
750
+ `a`-`f` range. The `base_prefix` and `base_postfix` values are never modified.
751
+ +
752
+ Use `:numbers_only` to uppercase generated hexadecimal digits without changing
753
+ separator characters. It has no effect for other bases.
615
754
  +
616
755
  .Uppercase hexadecimal output
617
756
  [example]
618
757
  ====
619
- "48879" with `base: 16, hex_capital: true` => "0xBE,EF"
758
+ "48879" with `base: 16, hex_capital: true, group_digits: 2` => "0xBE,EF"
759
+ "48879" with `base: 16, hex_capital: :numbers_only, group_digits: 2, group: "e"` => "0xBEeEF"
620
760
  ====
621
761
  +
622
762
  WARNING: If you use separator characters in the range `a`-`f` (such as `decimal: "d"` or `fraction_group: "f"`), those separators will also be uppercased when `hex_capital: true`. Choose separators outside this range (e.g., `".", ",", " ", "_"`) if you need them to remain unchanged.
@@ -624,9 +764,9 @@ WARNING: If you use separator characters in the range `a`-`f` (such as `decimal:
624
764
  .Separator uppercasing behavior
625
765
  [example]
626
766
  ====
627
- "48879" with `base: 16, hex_capital: true, group: "g"` => "0xBEgEF" (g remains lowercase)
767
+ "48879" with `base: 16, hex_capital: true, group_digits: 2, group: "g"` => "0xBEgEF" (g remains lowercase)
628
768
 
629
- "48879" with `base: 16, hex_capital: true, group: "f"` => "0xBEFEF" (f separator becomes F)
769
+ "48879" with `base: 16, hex_capital: true, group_digits: 2, group: "f"` => "0xBEFEF" (f separator becomes F)
630
770
  ====
631
771
 
632
772
  NOTE: When `base` is not 10, both the integer and fractional parts are converted to the specified base.
@@ -638,7 +778,10 @@ as decimal `0.75` for the fractional part) and converts that real value to the r
638
778
  ===== Base conversion with other formatting options
639
779
 
640
780
  Base conversion works seamlessly with other formatting options such as `precision`, `digit_count`,
641
- `fraction_group`, `fraction_group_digits`, and `significant`.
781
+ `fraction_group`, `fraction_group_digits`, `significant`, and integer padding.
782
+ +
783
+ Integer padding is applied after `digit_count` rounding, so padding can add
784
+ integer digits after the total visible-digit budget has been resolved.
642
785
 
643
786
  .Base conversion with precision
644
787
  [example]
@@ -696,8 +839,44 @@ right. Accepts an integer value.
696
839
  "32232.232131" => "32232.23 21 31"
697
840
  ====
698
841
 
842
+ [[format_option_significant]]
699
843
  `significant`:: (`Numeric` value)
700
844
  Sets the number of significant digits to show, with the value rounded.
845
+ Leading zeros before the first non-zero digit are not counted as significant.
846
+ +
847
+ .Using significant digits
848
+ [example]
849
+ ====
850
+ "112" with `significant: 2` => "110"
851
+ "1999" with `significant: 2` => "2,000"
852
+ "0.1999" with `significant: 3` => "0.200"
853
+ ====
854
+ +
855
+ Significant-digit rounding is applied before localized rendering. This means
856
+ the formatter does not re-parse an already grouped or localized output string,
857
+ so `decimal` and `group` may use the same symbol without changing which digits
858
+ are rounded.
859
+ +
860
+ .Using significant digits with overlapping decimal and group symbols
861
+ [example]
862
+ ====
863
+ "327428.7432878432992" with `decimal: ","`, `group_digits: 3`, `fraction_group_digits: 2`, `fraction_group: "'"`, `significant: 9`
864
+ => "327,428,74'3"
865
+ ====
866
+ +
867
+ When `base` is not 10 and `precision` is omitted, Plurimath infers the
868
+ fractional precision needed in the target base where possible. That inference
869
+ is capped by the source number's significant digits, so finite converted values
870
+ can keep required trailing zeros while repeating fractional conversions are not
871
+ expanded only to fill the requested count.
872
+ +
873
+ .Using significant digits with base conversion
874
+ [example]
875
+ ====
876
+ "48879" with `base: 16, significant: 3, group_digits: 2, group: ","` => "0xbe,f0"
877
+ "123.25" with `base: 16, significant: 5, group_digits: 10` => "0x7b.400"
878
+ "0.1" with `base: 16, significant: 5, group_digits: 10` => "0x0.1"
879
+ ====
701
880
 
702
881
  `notation`:: (`String` value)
703
882
  Specifies the mathematical notation to be used. Accepts the following values.
@@ -751,12 +930,12 @@ modes: `scientific` and `engineering`). Defaults to `×`.
751
930
  ----
752
931
  ====
753
932
 
754
- `exponent_sign`:: (`String` value)
933
+ `exponent_sign`:: (`Symbol` or `String` value)
755
934
  Whether to use a plus sign to indicate positive exponents, in exponent-based
756
935
  notation (used in the modes: `e`, `scientific`, `engineering`). Legal values
757
936
  are:
758
937
 
759
- `plus`::: The `+` symbol is used.
938
+ `:plus`, `"plus"`::: The `+` symbol is used.
760
939
  +
761
940
  .Using the plus sign to indicate positive exponents
762
941
  [example]
@@ -773,13 +952,27 @@ These options are to be grouped under a single Hash object.
773
952
  ----
774
953
  {
775
954
  decimal: ",", # replaces the decimal point with the passed string
776
- group_digits: 2, # groups integer part into passed integer
777
- group: "'", # places the string between grouped parts of the integer
778
- fraction_group_digits: 3, # groups fraction part into passed integer
779
- fraction_group: ",", # places the string between grouped parts of the fraction
955
+ group_digits: 2, # groups integer digits into groups of the passed size
956
+ group: "'", # places the string between grouped integer digits
957
+ fraction_group_digits: 3, # groups fraction digits into groups of the passed size
958
+ fraction_group: ",", # places the string between grouped fraction digits
959
+ number_sign: :plus, # adds a plus sign to positive numbers
960
+ digit_count: 6, # limits the total rendered digit count
961
+ significant: 4, # limits the rendered significant digit count
962
+ precision: 2, # controls rendered fractional precision
963
+ notation: :scientific, # renders the number using the selected notation
964
+ e: "E", # sets the exponent marker for E notation
965
+ times: "x", # sets the multiplication symbol for power notation
966
+ exponent_sign: :plus, # adds a plus sign to positive exponents
967
+ base: 16, # renders the number in the selected base
968
+ base_prefix: "0x", # prefixes non-decimal base output when no postfix key is present
969
+ base_postfix: "_16", # appends a postfix and takes precedence over base_prefix
970
+ hex_capital: :numbers_only, # uppercases generated hexadecimal digits only
780
971
  }
781
972
  ----
782
973
 
974
+ Only include the keys needed for the formatter behavior being configured.
975
+
783
976
 
784
977
  [[localize_number]]
785
978
  ===== Via the `localize_number` option
@@ -1131,13 +1324,15 @@ The steps to format numbers within a formula are:
1131
1324
  . Create a number formatter that can be configured;
1132
1325
 
1133
1326
  . Apply the number formatter to a formula through the `Formula.to_{format}`
1134
- method using a `formatter` option, which serializes the formula into an math
1135
- representation language.
1327
+ method using a `formatter` option, or configure a default formatter through
1328
+ `Plurimath.configure`.
1136
1329
 
1137
1330
  The formatter should be an instance of `Plurimath::NumberFormatter` or a custom
1138
1331
  formatter derived from `Plurimath::Formatter::Standard`. Custom formatters can
1139
- define a `format(formula, number)` method to make context-aware formatting
1140
- decisions based on the surrounding formula (see <<Contextual number formatting>>).
1332
+ define a `format_number(formula, number)` method to make context-aware
1333
+ formatting decisions based on the surrounding formula (see
1334
+ <<Contextual number formatting>>). Existing custom formatters that define
1335
+ `format(formula, number)` remain supported.
1141
1336
 
1142
1337
  The quick example below demonstrates how to format a number in a formula.
1143
1338
 
@@ -1191,6 +1386,58 @@ print formula.to_mathml(formatter: custom_formatter)
1191
1386
  ----
1192
1387
  ====
1193
1388
 
1389
+ [example]
1390
+ .Configuring a default number formatter for formula rendering
1391
+ ====
1392
+ [source,ruby]
1393
+ ----
1394
+ Plurimath.configure do |config|
1395
+ config.number_formatter = Plurimath::Formatter::Standard.new
1396
+ end
1397
+
1398
+ formula = Plurimath::Math.parse("10000 + 2000", :asciimath)
1399
+ formula.to_latex
1400
+ # => "10,000 + 2,000"
1401
+ ----
1402
+
1403
+ Passing `formatter:` to a formula serialization method takes precedence over
1404
+ the configured default formatter.
1405
+ ====
1406
+
1407
+
1408
+ ==== Deprecation notices
1409
+
1410
+ Plurimath exposes deprecation behavior through the shared configuration object.
1411
+ The current number formatter arguments documented above remain supported; this
1412
+ setting controls how Plurimath handles deprecation notices when a deprecated
1413
+ feature is used.
1414
+
1415
+ [source,ruby]
1416
+ ----
1417
+ Plurimath.configure do |config|
1418
+ config.deprecation.behavior = :collect
1419
+ end
1420
+
1421
+ # Retrieve collected notices after parsing
1422
+ Plurimath::Deprecation.notices.each do |notice|
1423
+ puts notice.to_s # => "[plurimath][DEPRECATION] ..."
1424
+ puts notice.severity # => :warning
1425
+ puts notice.feature # => "feature_name"
1426
+ end
1427
+ ----
1428
+
1429
+ Supported behaviors are:
1430
+
1431
+ `collect`:: Default behavior. Collects deprecation notices in
1432
+ `Plurimath::Deprecation.notices` for the caller to retrieve. Each notice is a
1433
+ `Plurimath::DeprecationError` with `severity`, `feature`, `replacement`,
1434
+ `since`, `remove_in`, and `to_s`.
1435
+
1436
+ `raise`:: Raises `Plurimath::DeprecationError` immediately when a deprecated
1437
+ feature is used.
1438
+
1439
+ `silence`:: Suppresses deprecation notices entirely.
1440
+
1194
1441
 
1195
1442
  ==== Defining a number formatter
1196
1443
 
@@ -1231,15 +1478,29 @@ There are two types of number formatting configuration to change:
1231
1478
 
1232
1479
  . Overriding options through the `options` argument.
1233
1480
 
1481
+ .Syntax for creating a `Plurimath::Formatter::Standard` object
1482
+ [source,ruby]
1483
+ ----
1484
+ formatter = Plurimath::Formatter::Standard.new(
1485
+ locale: :en,
1486
+ string_format: nil,
1487
+ options: {},
1488
+ precision: nil,
1489
+ )
1490
+ ----
1491
+
1234
1492
  The arguments are:
1235
1493
 
1236
1494
  `locale`:: (default: `:en` for English) a symbol or string value. The supported
1237
1495
  locales are listed in the link:/blog/2024-07-09-number-formatter[number formatter blog post].
1238
1496
 
1239
- `options`:: (default: empty) a hash of options (`localizer_symbols`). The options
1240
- are listed in the link:/blog/2024-07-09-number-formatter[number formatter blog post].
1497
+ `options`:: (default: empty) a hash of options. This is the
1498
+ `Plurimath::Formatter::Standard` alias for
1499
+ `Plurimath::NumberFormatter`'s `localizer_symbols` argument.
1241
1500
 
1242
- `string_format`:: (default: `nil`, disabled) a string value (`localize_number`)
1501
+ `string_format`:: (default: `nil`, disabled) a string value. This is the
1502
+ `Plurimath::Formatter::Standard` alias for
1503
+ `Plurimath::NumberFormatter`'s `localize_number` argument.
1243
1504
 
1244
1505
  `precision`:: (default: `nil`, disabled) an integer value.
1245
1506
 
@@ -1282,7 +1543,7 @@ The custom formatter is to be subclassed from `Plurimath::Formatter::Standard`.
1282
1543
  [source,ruby]
1283
1544
  ----
1284
1545
  class MyCustomFormatter < Plurimath::Formatter::Standard <1>
1285
- def initialize(locale:, precision:, options:, string_format:) <2>
1546
+ def initialize(locale: :en, precision: nil, options: {}, string_format: nil) <2>
1286
1547
  super
1287
1548
  end
1288
1549
  end
@@ -1297,7 +1558,7 @@ The default options of the custom formatter are set using the
1297
1558
  [source,ruby]
1298
1559
  ----
1299
1560
  class MyCustomFormatter < Plurimath::Formatter::Standard
1300
- def initialize(locale:, precision:, options:, string_format:)
1561
+ def initialize(locale: :en, precision: nil, options: {}, string_format: nil)
1301
1562
  super
1302
1563
  end
1303
1564
 
@@ -1354,24 +1615,30 @@ In some cases, certain numbers within a formula should be formatted differently
1354
1615
  depending on their context. For example, a year like "2024" should not have digit
1355
1616
  grouping applied, while other numbers in the same formula should.
1356
1617
 
1357
- The `format` method on the formatter receives both the root
1618
+ The `format_number` method on the formatter receives both the root
1358
1619
  `Plurimath::Math::Formula` tree and the current `Plurimath::Math::Number` node
1359
1620
  being processed, allowing context-aware formatting decisions.
1360
1621
 
1361
- .Signature of the `format` method
1622
+ .Signature of the `format_number` method
1362
1623
  [source,ruby]
1363
1624
  ----
1364
- def format(formula, number)
1625
+ def format_number(formula, number)
1365
1626
  # formula: the root Plurimath::Math::Formula containing the full equation
1366
1627
  # number: the Plurimath::Math::Number node currently being formatted
1367
1628
  # returns: a formatted string
1368
1629
  end
1369
1630
  ----
1370
1631
 
1371
- Formatters that do not define `format` will fall back to `localized_number`, so
1372
- existing formatters continue to work without changes.
1632
+ Formula rendering uses the first formatter method available in this order:
1633
+ `format_number(formula, number)`, then `format(formula, number)`, then
1634
+ `localized_number(number.value.to_s)`. This keeps existing custom formatters
1635
+ working while giving new custom formatters a method name specific to formula
1636
+ number formatting.
1373
1637
 
1374
- To implement contextual formatting, define a `format` method in a custom
1638
+ New custom formatters should define `format_number`; the `format` fallback
1639
+ exists for compatibility with existing custom formatters.
1640
+
1641
+ To implement contextual formatting, define a `format_number` method in a custom
1375
1642
  formatter subclass.
1376
1643
 
1377
1644
  .Creating a year-aware formatter that skips digit grouping for year-like numbers
@@ -1380,7 +1647,7 @@ formatter subclass.
1380
1647
  [source,ruby]
1381
1648
  ----
1382
1649
  class YearFormatter < Plurimath::Formatter::Standard
1383
- def format(formula, number) <1>
1650
+ def format_number(formula, number) <1>
1384
1651
  int_value = Integer(number.value, exception: false)
1385
1652
  if int_value && int_value > 1800 && int_value < 2200 <2>
1386
1653
  number.value.to_s
@@ -1395,7 +1662,7 @@ formula = Plurimath::Math.parse("2024 + 1000000", :asciimath)
1395
1662
  formula.to_latex(formatter: formatter)
1396
1663
  # => "2024 + 1,000,000" <4>
1397
1664
  ----
1398
- <1> Define `format` to receive the formula tree and number node.
1665
+ <1> Define `format_number` to receive the formula tree and number node.
1399
1666
  <2> Detect year-like numbers and return them unformatted.
1400
1667
  <3> Call `localized_number` to apply locale-based formatting for other numbers.
1401
1668
  <4> "2024" is left as-is, while "1000000" is formatted with digit grouping.
@@ -1405,8 +1672,8 @@ formula.to_latex(formatter: formatter)
1405
1672
  [[standard_configuration]]
1406
1673
  === Default number formatting configuration
1407
1674
 
1408
- The default configuration for formatting numbers is as follows, set in the
1409
- `Plurimath::Formatter::Standard` class.
1675
+ The effective default configuration for `Plurimath::Formatter::Standard` is as
1676
+ follows.
1410
1677
 
1411
1678
  |===
1412
1679
  |Option key |Description |Value
@@ -1421,7 +1688,11 @@ The default configuration for formatting numbers is as follows, set in the
1421
1688
 
1422
1689
  |`exponent_sign`
1423
1690
  |The sign used for the exponent part of the number
1424
- |`"plus"`
1691
+ |`nil`
1692
+
1693
+ |`number_sign`
1694
+ |The sign used for positive numbers
1695
+ |`nil`
1425
1696
 
1426
1697
  |`fraction_group`
1427
1698
  |The character used to separate groups of digits in the fraction part
@@ -1435,6 +1706,18 @@ The default configuration for formatting numbers is as follows, set in the
1435
1706
  |The number of digits in each group of the integer part
1436
1707
  |`3`
1437
1708
 
1709
+ |`padding`
1710
+ |The character used to pad the integer part
1711
+ |`"0"`
1712
+
1713
+ |`padding_digits`
1714
+ |The minimum number of integer digits to display
1715
+ |`0`
1716
+
1717
+ |`padding_group_digits`
1718
+ |The multiple of integer digits to pad to
1719
+ |`0`
1720
+
1438
1721
  |`significant`
1439
1722
  |The number of significant digits to display
1440
1723
  |`0`
@@ -1445,7 +1728,23 @@ The default configuration for formatting numbers is as follows, set in the
1445
1728
 
1446
1729
  |`precision`
1447
1730
  |The number of decimal places to display
1448
- |`0`
1731
+ |`nil`
1732
+
1733
+ |`base`
1734
+ |The numeric base used to render the number
1735
+ |`10`
1736
+
1737
+ |`base_prefix`
1738
+ |The prefix used for non-decimal bases
1739
+ |standard prefix for the selected base
1740
+
1741
+ |`base_postfix`
1742
+ |The postfix used for non-decimal bases
1743
+ |`nil`
1744
+
1745
+ |`hex_capital`
1746
+ |Whether hexadecimal output is capitalized
1747
+ |`nil`
1449
1748
 
1450
1749
  |`decimal`
1451
1750
  |The character used as the decimal separator
@@ -1460,7 +1759,7 @@ The default configuration for formatting numbers is as follows, set in the
1460
1759
  |`"x"`
1461
1760
 
1462
1761
  |`e`
1463
- |The character used for exponentiation
1762
+ |The character used for E notation
1464
1763
  |`"e"`
1465
1764
 
1466
1765
  |===
@@ -10,7 +10,7 @@ module Plurimath
10
10
  rule(:comma) { str(",") >> space? }
11
11
  rule(:space?) { space.maybe }
12
12
  rule(:number) do
13
- (match("[0-9]").repeat(1) >> str(".") >> match("[0-9]").repeat(1)).as(:number) |
13
+ (match("[0-9]").repeat(1) >> decimal_marker >> match("[0-9]").repeat(1)).as(:number) |
14
14
  match("[0-9]").repeat(1).as(:number) |
15
15
  str(".").as(:symbol)
16
16
  end
@@ -78,7 +78,7 @@ module Plurimath
78
78
  binary_classes |
79
79
  ternary_classes |
80
80
  hash_to_expression(Constants.precompile_constants) |
81
- (match(/[0-9]/).as(:number) >> str(",").as(:comma)).repeat(1).as(:comma_separated) |
81
+ (match(/[0-9]/).as(:number) >> (decimal_marker >> match("[0-9]")).absent? >> str(",").as(:comma)).repeat(1).as(:comma_separated) |
82
82
  quoted_text |
83
83
  (str("d").as(:d) >> str("x").as(:x)).as(:intermediate_exp) |
84
84
  ((str("left").absent? >> str("right").absent?) >> match["a-zA-Z"].as(:symbol)) |
@@ -196,6 +196,10 @@ module Plurimath
196
196
  end
197
197
  end
198
198
 
199
+ def decimal_marker
200
+ str(Plurimath.configuration.decimal)
201
+ end
202
+
199
203
  def unary_functions(first_value)
200
204
  if ["'underbrace'", "'ubrace'"].include?(first_value.to_s)
201
205
  (first_value.as(:unary_class) >> space? >> str("_").as(:symbol)).as(:unary) |
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Plurimath
4
+ class Configuration
5
+ DEFAULT_DECIMAL = "."
6
+
7
+ attr_accessor :number_formatter, :locale
8
+
9
+ def deprecation
10
+ Deprecation
11
+ end
12
+
13
+ def decimal
14
+ Formatter::SupportedLocales.decimal_for(locale, default: DEFAULT_DECIMAL)
15
+ end
16
+ end
17
+ end