Skip to content

Commit d902b68

Browse files
authored
Merge pull request #40 from gunthercox/decimal
Add support for "point" as a binary word operator
2 parents b30a51f + c5e64c2 commit d902b68

8 files changed

Lines changed: 286 additions & 34 deletions

File tree

docs/advanced.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ mathparse supports unary functions that operate on a single value:
6464
* - log
6565
- Base-10 logarithm
6666
- ``mathparse.parse('log 100')`` → 2.0
67+
* - neg
68+
- Negative (unary minus)
69+
- ``mathparse.parse('negative five', language='ENG')`` → -5
6770

6871
**Examples:**
6972

docs/examples.rst

Lines changed: 103 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,90 @@ Word-Based Function Examples
275275
result = mathparse.parse('three squared times two', language='ENG')
276276
print(result) # 18
277277
278+
Decimal Numbers
279+
---------------
280+
281+
Word-Based Decimal Numbers
282+
++++++++++++++++++++++++++
283+
284+
mathparse supports parsing decimal numbers expressed in words using the word "point" (or equivalent in other languages).
285+
The decimal point is treated as a binary operator that combines the integer and fractional parts.
286+
287+
English Decimal Examples
288+
~~~~~~~~~~~~~~~~~~~~~~~~
289+
290+
.. code-block:: python
291+
292+
from mathparse import mathparse
293+
294+
# Simple decimal numbers
295+
result = mathparse.parse('five point two', language='ENG')
296+
print(result) # 5.2
297+
298+
result = mathparse.parse('ten point twenty five', language='ENG')
299+
print(result) # 10.25
300+
301+
result = mathparse.parse('fifty three point four', language='ENG')
302+
print(result) # 53.4
303+
304+
# Decimal numbers in expressions
305+
result = mathparse.parse('five point two plus three', language='ENG')
306+
print(result) # 8.2
307+
308+
result = mathparse.parse('fifty three point four times seven', language='ENG')
309+
print(result) # 373.8
310+
311+
result = mathparse.parse('three point five plus two point one', language='ENG')
312+
print(result) # 5.6
313+
314+
result = mathparse.parse('twenty one point six divided by four', language='ENG')
315+
print(result) # 5.4
316+
317+
French Decimal Examples
318+
~~~~~~~~~~~~~~~~~~~~~~~
319+
320+
In French, the word "virgule" (comma) is used for decimal points:
321+
322+
.. code-block:: python
323+
324+
# French decimal numbers
325+
result = mathparse.parse('cinq virgule deux', language='FRE')
326+
print(result) # 5.2
327+
328+
result = mathparse.parse('dix virgule vingt cinq', language='FRE')
329+
print(result) # 10.25
330+
331+
Spanish Decimal Examples
332+
~~~~~~~~~~~~~~~~~~~~~~~~
333+
334+
In Spanish, the word "punto" is used for decimal points:
335+
336+
.. code-block:: python
337+
338+
# Spanish decimal numbers
339+
result = mathparse.parse('cinco punto dos', language='ESP')
340+
print(result) # 5.2
341+
342+
result = mathparse.parse('diez punto veinticinco', language='ESP')
343+
print(result) # 10.25
344+
345+
Numeric Decimal Support
346+
+++++++++++++++++++++++
347+
348+
mathparse also supports standard numeric decimal notation:
349+
350+
.. code-block:: python
351+
352+
# Numeric decimals
353+
result = mathparse.parse('5.2')
354+
print(result) # 5.2
355+
356+
result = mathparse.parse('53.4 * 7')
357+
print(result) # 373.8
358+
359+
result = mathparse.parse('3.5 + 2.1')
360+
print(result) # 5.6
361+
278362
Practical Use Cases
279363
-------------------
280364

@@ -324,10 +408,10 @@ Natural Language Processing
324408
try:
325409
# Extract the mathematical expression
326410
expression = mathparse.extract_expression(sentence, language)
327-
411+
328412
# Parse and calculate
329413
result = mathparse.parse(expression, language=language)
330-
414+
331415
return {
332416
'original': sentence,
333417
'extracted': expression,
@@ -359,18 +443,18 @@ Unit Conversion Helper
359443
360444
def convert_units():
361445
"""Examples of using mathparse for unit conversions."""
362-
446+
363447
# Temperature conversion: Celsius to Fahrenheit
364448
# F = C * 9/5 + 32
365449
celsius = 25
366450
fahrenheit = mathparse.parse(f'{celsius} * 9 / 5 + 32')
367451
print(f"{celsius}°C = {fahrenheit}°F") # 25°C = 77.0°F
368-
452+
369453
# Area of circle: π * r²
370454
radius = 5
371455
area = mathparse.parse(f'pi * {radius} * {radius}')
372456
print(f"Circle area (r={radius}): {area}") # Circle area (r=5): 78.54225
373-
457+
374458
# Compound interest: P * (1 + r)^t
375459
principal = 1000
376460
rate = 0.05 # 5%
@@ -392,13 +476,13 @@ Educational Applications
392476
("What is deux plus trois?", 'FRE', 5), # French
393477
("Calculate fünf mal sechs", 'GER', 30), # German
394478
]
395-
479+
396480
for question, lang, expected in questions:
397481
try:
398482
# Extract and solve
399483
expression = mathparse.extract_expression(question, lang)
400484
result = mathparse.parse(expression, language=lang)
401-
485+
402486
correct = "" if result == expected else ""
403487
print(f"{correct} {question}")
404488
print(f" Expression: {expression}")
@@ -456,13 +540,13 @@ Robust Error Handling
456540
"""Safely parse expressions with comprehensive error handling."""
457541
try:
458542
result = mathparse.parse(expression, language=language)
459-
543+
460544
# Check for division by zero
461545
if result == 'undefined':
462546
return {'success': False, 'error': 'Division by zero', 'result': default}
463-
547+
464548
return {'success': True, 'result': result}
465-
549+
466550
except InvalidLanguageCodeException:
467551
return {'success': False, 'error': 'Invalid language code', 'result': default}
468552
except PostfixTokenEvaluationException as e:
@@ -492,20 +576,22 @@ Benchmarking Example
492576
from mathparse import mathparse
493577
494578
def benchmark_parsing():
495-
"""Compare performance of numeric vs word-based parsing."""
496-
579+
"""
580+
Compare performance of numeric vs word-based parsing.
581+
"""
582+
497583
# Numeric expressions (faster)
498584
start_time = time.time()
499585
for i in range(1000):
500586
result = mathparse.parse('2 + 3 * 4')
501587
numeric_time = time.time() - start_time
502-
588+
503589
# Word-based expressions (slower due to text processing)
504590
start_time = time.time()
505591
for i in range(1000):
506592
result = mathparse.parse('two plus three times four', language='ENG')
507593
word_time = time.time() - start_time
508-
594+
509595
print(f"Numeric parsing: {numeric_time:.4f}s for 1000 operations")
510596
print(f"Word-based parsing: {word_time:.4f}s for 1000 operations")
511597
print(f"Word-based is {word_time/numeric_time:.1f}x slower")
@@ -541,22 +627,22 @@ Optimization Tips
541627
def batch_parse(expressions, language=None):
542628
"""Parse multiple expressions efficiently."""
543629
results = []
544-
630+
545631
# Pre-validate language if provided
546632
if language:
547633
try:
548634
word_groups_for_language(language)
549635
except InvalidLanguageCodeException:
550636
return [{'error': 'Invalid language code'}] * len(expressions)
551-
637+
552638
# Process all expressions
553639
for expr in expressions:
554640
try:
555641
result = mathparse.parse(expr, language=language)
556642
results.append({'success': True, 'result': result})
557643
except Exception as e:
558644
results.append({'success': False, 'error': str(e)})
559-
645+
560646
return results
561647
562648
# Example usage

docs/languages.rst

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ The following languages are supported with their ISO 639-2 language codes:
1515
* - Code
1616
- Language
1717
- Example
18+
* - CHI
19+
- Simplified Chinese
20+
- ``'五十 乘 二十 加 十'``
1821
* - DUT
1922
- Dutch
2023
- ``'vijftig maal twintig plus tien'``
@@ -273,6 +276,41 @@ Thai (THA)
273276
mathparse.parse('สี่ ยกกำลังสอง', language='THA')
274277
>>> 16
275278
279+
Simplified Chinese (CHI)
280+
++++++++++++++++++++++++
281+
282+
.. code-block:: python
283+
284+
# Basic arithmetic using Chinese characters
285+
mathparse.parse('五 加 三', language='CHI')
286+
>>> 8
287+
288+
# Multiplication
289+
mathparse.parse('六 乘 九', language='CHI')
290+
>>> 54
291+
292+
# Using alternative number representations
293+
mathparse.parse('五十 加上 二十', language='CHI')
294+
>>> 70
295+
296+
# Large numbers with scales
297+
mathparse.parse('三 百 加 五十', language='CHI')
298+
>>> 350
299+
300+
mathparse.parse('两 千 五 百', language='CHI')
301+
>>> 2500
302+
303+
# Powers and roots
304+
mathparse.parse('四 平方', language='CHI')
305+
>>> 16
306+
307+
mathparse.parse('平方根 十六', language='CHI')
308+
>>> 4.0
309+
310+
# Negative numbers
311+
mathparse.parse('负 五 加 十', language='CHI')
312+
>>> 5
313+
276314
Common Operators by Language
277315
----------------------------
278316
@@ -298,6 +336,9 @@ English Operators
298336
* - Division
299337
- divided by
300338
- ``'twenty divided by four'``
339+
* - Decimal Point
340+
- point
341+
- ``'five point two'``
301342
* - Power
302343
- to the power of
303344
- ``'two to the power of three'``
@@ -310,6 +351,9 @@ English Operators
310351
* - Square Root
311352
- square root of
312353
- ``'square root of nine'``
354+
* - Negative
355+
- negative
356+
- ``'negative five'``
313357
314358
French Operators
315359
++++++++++++++++
@@ -333,6 +377,9 @@ French Operators
333377
* - Division
334378
- divisé par
335379
- ``'vingt divisé par quatre'``
380+
* - Decimal Point
381+
- virgule
382+
- ``'cinq virgule deux'``
336383
* - Power
337384
- à la puissance
338385
- ``'deux à la puissance trois'``

docs/postfix.rst

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,51 @@ The mathparse library converts infix expressions to postfix using the `Shunting-
115115
result = evaluate_postfix(postfix) # 11
116116
117117
The conversion process respects operator precedence:
118-
- Higher precedence: ``^`` (exponentiation), ``*``, ``/``
119-
- Lower precedence: ``+``, ``-``
118+
- Highest precedence: ``.`` (decimal point)
119+
- Higher precedence: ``^`` (exponentiation)
120+
- Medium precedence: ``*``, ``/`` (multiplication, division)
121+
- Lower precedence: ``+``, ``-`` (addition, subtraction)
120122
- Parentheses override natural precedence
121123

124+
Decimal Point Operator
125+
~~~~~~~~~~~~~~~~~~~~~~~
126+
127+
The decimal point (``.``) is treated as a special binary operator with the highest precedence.
128+
It combines an integer part and a fractional part to create a decimal number.
129+
130+
**How it works:**
131+
132+
.. code-block:: python
133+
134+
# "53 . 4" is evaluated as: 53 + (4 / 10^1) = 53.4
135+
result = mathparse.parse('53 . 4')
136+
# Returns: 53.4
137+
138+
# "10 . 25" is evaluated as: 10 + (25 / 10^2) = 10.25
139+
result = mathparse.parse('10 . 25')
140+
# Returns: 10.25
141+
142+
**Postfix evaluation:**
143+
144+
.. code-block:: text
145+
146+
Expression: 53 . 4
147+
Postfix: [53, 4, '.']
148+
149+
Evaluation:
150+
Token: 53 Stack: [53]
151+
Token: 4 Stack: [53, 4]
152+
Token: . Stack: [53.4] (combines 53 and 4 into 53.4)
153+
Result: 53.4
154+
155+
The decimal operator has the highest precedence to ensure it binds tightly before any other operations:
156+
157+
.. code-block:: python
158+
159+
# "5 . 2 + 3" is parsed as "(5.2) + 3", not "5 . (2 + 3)"
160+
result = mathparse.parse('5 . 2 + 3')
161+
# Returns: 8.2
162+
122163
123164
Security: Avoiding eval() Vulnerabilities
124165
-----------------------------------------

0 commit comments

Comments
 (0)