Skip to content

Commit

Permalink
Handle fractions
Browse files Browse the repository at this point in the history
  • Loading branch information
swaroopch authored and bfontaine committed Aug 26, 2018
1 parent 7a3865c commit d35301e
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 4 deletions.
3 changes: 3 additions & 0 deletions edn_format/edn_dump.py
Expand Up @@ -3,6 +3,7 @@

import datetime
import decimal
import fractions
import itertools
import re
import sys
Expand Down Expand Up @@ -105,6 +106,8 @@ def udump(obj,
pairs = ((Keyword(k) if isinstance(k, (bytes, basestring)) else k, v) for k, v in pairs)

return '{{{}}}'.format(seq(itertools.chain.from_iterable(pairs), **kwargs))
elif isinstance(obj, fractions.Fraction):
return '{}/{}'.format(obj.numerator, obj.denominator)
elif isinstance(obj, datetime.datetime):
return '#inst "{}"'.format(pyrfc3339.generate(obj, microseconds=True))
elif isinstance(obj, datetime.date):
Expand Down
13 changes: 11 additions & 2 deletions edn_format/edn_lex.py
Expand Up @@ -5,6 +5,7 @@
# see http://stackoverflow.com/a/24519338
import codecs
import decimal
import fractions
import logging
import re
import sys
Expand Down Expand Up @@ -93,6 +94,7 @@ def __str__(self):
'BOOLEAN',
'INTEGER',
'FLOAT',
'RATIO',
'SYMBOL',
'KEYWORD',
'VECTOR_START',
Expand Down Expand Up @@ -124,10 +126,10 @@ def __str__(self):
r"\/"
"[{all}]+"
"|"
r"\/"
"|"
"{start}"
"[{all}]*"
"|"
"/"
")").format(**PARTS)
KEYWORD = (":"
"("
Expand Down Expand Up @@ -215,6 +217,13 @@ def t_FLOAT(t):
return t


def t_RATIO(t):
r"""-?\d+/\d+"""
numerator, denominator = t.value.split("/", 1)
t.value = fractions.Fraction(int(numerator), int(denominator))
return t


def t_INTEGER(t):
r"""[+-]?\d+N?"""
if t.value.endswith('N'):
Expand Down
6 changes: 6 additions & 0 deletions edn_format/edn_parse.py
Expand Up @@ -91,6 +91,11 @@ def p_empty_map(p):
p[0] = ImmutableDict({})


def p_fraction(p):
"""fraction : RATIO"""
p[0] = p[1]


def p_map(p):
"""map : MAP_START expressions MAP_OR_SET_END"""
terms = p[2]
Expand All @@ -115,6 +120,7 @@ def p_expression(p):
| list
| set
| map
| fraction
| term"""
p[0] = p[1]

Expand Down
23 changes: 21 additions & 2 deletions tests.py
Expand Up @@ -6,8 +6,8 @@
from uuid import uuid4
import random
import datetime
import fractions
import unittest

import pytz

from edn_format import edn_lex, edn_parse, \
Expand Down Expand Up @@ -66,6 +66,7 @@ def test_lexer(self):
"true.")
self.check_lex("[LexToken(SYMBOL,Symbol($:ABC?),1,0)]",
"$:ABC?")

self.check_lex("[LexToken(MAP_START,'{',1,0), "
"LexToken(KEYWORD,Keyword(a),1,2), "
"LexToken(BOOLEAN,False,1,5), "
Expand All @@ -74,6 +75,9 @@ def test_lexer(self):
"LexToken(MAP_OR_SET_END,'}',1,21)]",
"{ :a false, :b false }")

self.check_lex("[LexToken(RATIO,Fraction(2, 3),1,0)]",
"2/3")

def check_parse(self, expected_output, actual_input):
self.assertEqual(expected_output, edn_parse.parse(actual_input))

Expand Down Expand Up @@ -129,6 +133,8 @@ def test_parser(self):
self.check_parse(frozenset({ImmutableList([u"ab", u"cd"]),
ImmutableList([u"ef"])}),
'#{["ab", "cd"], ["ef"]}')
self.check_parse(fractions.Fraction(2, 3), "2/3")
self.check_parse((2, Symbol('/'), 3), "(2 / 3)")

def check_roundtrip(self, data_input, **kw):
self.assertEqual(data_input, loads(dumps(data_input, **kw)))
Expand Down Expand Up @@ -234,7 +240,10 @@ def test_round_trip_same(self):
'#date "19/07/1984"',
'#{{"a" 1}}',
'#{{"a" #{{:b 2}}}}',
'"|"'
'"|"',
"/",
"1/3",
'"1/3"',
)

class TagDate(TaggedElement):
Expand Down Expand Up @@ -269,6 +278,16 @@ def test_exceptions(self):
with self.assertRaises(EDNDecodeError):
loads("{")

def test_fractions(self):
for edn_data in (
'0/1',
'1/1',
'1/2',
'1/3',
'99999999999999999999999999999999999/999999999999999999999999991',
):
self.assertEqual(edn_data, dumps(loads(edn_data)), edn_data)

def test_keyword_keys(self):
unchanged = (
None,
Expand Down

0 comments on commit d35301e

Please sign in to comment.