romanNumerals.py 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. # romanNumerals.py
  2. #
  3. # Copyright (c) 2006, Paul McGuire
  4. #
  5. from pyparsing import *
  6. def romanNumeralLiteral(numeralString, value):
  7. return Literal(numeralString).setParseAction(replaceWith(value))
  8. one = romanNumeralLiteral("I",1)
  9. four = romanNumeralLiteral("IV",4)
  10. five = romanNumeralLiteral("V",5)
  11. nine = romanNumeralLiteral("IX",9)
  12. ten = romanNumeralLiteral("X",10)
  13. forty = romanNumeralLiteral("XL",40)
  14. fifty = romanNumeralLiteral("L",50)
  15. ninety = romanNumeralLiteral("XC",90)
  16. onehundred = romanNumeralLiteral("C",100)
  17. fourhundred = romanNumeralLiteral("CD",400)
  18. fivehundred = romanNumeralLiteral("D",500)
  19. ninehundred = romanNumeralLiteral("CM",900)
  20. onethousand = romanNumeralLiteral("M",1000)
  21. numeral = ( onethousand | ninehundred | fivehundred | fourhundred |
  22. onehundred | ninety | fifty | forty | ten | nine | five |
  23. four | one ).leaveWhitespace()
  24. romanNumeral = OneOrMore(numeral).setParseAction( lambda s,l,t : sum(t) )
  25. # unit tests
  26. def makeRomanNumeral(n):
  27. def addDigit(n,limit,c,s):
  28. n -= limit
  29. s += c
  30. return n,s
  31. ret = ""
  32. while n >= 1000: n,ret = addDigit(n,1000,"M",ret)
  33. while n >= 900: n,ret = addDigit(n, 900,"CM",ret)
  34. while n >= 500: n,ret = addDigit(n, 500,"D",ret)
  35. while n >= 400: n,ret = addDigit(n, 400,"CD",ret)
  36. while n >= 100: n,ret = addDigit(n, 100,"C",ret)
  37. while n >= 90: n,ret = addDigit(n, 90,"XC",ret)
  38. while n >= 50: n,ret = addDigit(n, 50,"L",ret)
  39. while n >= 40: n,ret = addDigit(n, 40,"XL",ret)
  40. while n >= 10: n,ret = addDigit(n, 10,"X",ret)
  41. while n >= 9: n,ret = addDigit(n, 9,"IX",ret)
  42. while n >= 5: n,ret = addDigit(n, 5,"V",ret)
  43. while n >= 4: n,ret = addDigit(n, 4,"IV",ret)
  44. while n >= 1: n,ret = addDigit(n, 1,"I",ret)
  45. return ret
  46. tests = " ".join(makeRomanNumeral(i) for i in range(1,5000+1))
  47. roman_int_map = {}
  48. expected = 1
  49. for t,s,e in romanNumeral.scanString(tests):
  50. orig = tests[s:e]
  51. if t[0] != expected:
  52. print("{0} {1} {2}".format("==>", t, orig))
  53. roman_int_map[orig] = t[0]
  54. expected += 1
  55. def verify_value(s, tokens):
  56. expected = roman_int_map[s]
  57. if tokens[0] != expected:
  58. raise Exception("incorrect value for {0} ({1}), expected {2}".format(s, tokens[0], expected ))
  59. romanNumeral.runTests("""\
  60. XVI
  61. XXXIX
  62. XIV
  63. XIX
  64. MCMLXXX
  65. MMVI
  66. """, fullDump=False,
  67. postParse=verify_value)