parser.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import lark
  2. import pprint
  3. import re
  4. import sys
  5. class LineDesc:
  6. def __init__(self, level, text, lineno):
  7. self.level = level
  8. self.text = text
  9. self.lineno = lineno
  10. assert self.text[0] != ' '
  11. def __repr__(self):
  12. return "<%s:'%s'>" % (self.level, self.text)
  13. def clean_line(line):
  14. lead_spaces = len(line) - len(line.lstrip(" "))
  15. level = lead_spaces / 4
  16. # Remove leading spaces
  17. if line.strip(" ") == "":
  18. return None
  19. line = line.lstrip(" ")
  20. # Remove all comments
  21. line = re.sub('#.*$', '', line).strip()
  22. if not line:
  23. return None
  24. return level, line
  25. def parse(text):
  26. lines = text.split("\n")
  27. linedescs = []
  28. # These are to join open parenthesis
  29. current_line = ""
  30. paren_level = 0
  31. for lineno, line in enumerate(lines):
  32. if (lineinfo := clean_line(line)) is None:
  33. continue
  34. level, line = lineinfo
  35. for c in line:
  36. if c == "(":
  37. paren_level += 1
  38. elif c == ")":
  39. paren_level -= 1
  40. #print(level, paren_level, current_line)
  41. if paren_level < 0:
  42. print("error: too many closing paren )", file=sys.stderr)
  43. print("line:", lineno)
  44. return
  45. if current_line:
  46. current_line += " " + line
  47. else:
  48. current_line = line
  49. if paren_level > 0:
  50. continue
  51. #print(level, current_line)
  52. ldesc = LineDesc(level, current_line, lineno)
  53. linedescs.append(ldesc)
  54. current_line = ""
  55. if paren_level > 0:
  56. print("error: missing closing paren )", file=sys.stderr)
  57. return None
  58. return linedescs
  59. def section(linedescs):
  60. sections = []
  61. current_section = None
  62. for desc in linedescs:
  63. if desc.level == 0:
  64. if current_section:
  65. sections.append(current_section)
  66. current_section = [desc]
  67. continue
  68. current_section.append(desc)
  69. sections.append(current_section)
  70. return sections
  71. def classify(sections):
  72. consts = []
  73. funcs = []
  74. contracts = []
  75. for section in sections:
  76. assert len(section)
  77. if section[0].text == "const:":
  78. consts.append(section)
  79. elif section[0].text.startswith("def"):
  80. funcs.append(section)
  81. elif section[0].text.startswith("contract"):
  82. contracts.append(section)
  83. return consts, funcs, contracts
  84. def tokenize_const(text):
  85. parser = lark.Lark(r"""
  86. value_map: NAME ":" type_def
  87. type_def: point
  88. | blake2s_personalization
  89. | pedersen_personalization
  90. | list
  91. point: "Point"
  92. blake2s_personalization: "Blake2sPersonalization"
  93. pedersen_personalization: "PedersenPersonalization"
  94. list: "list<" type_def ">"
  95. %import common.CNAME -> NAME
  96. %import common.WS
  97. %ignore WS
  98. """, start="value_map")
  99. return parser.parse(text)
  100. def read_consts(consts):
  101. for subsection in consts:
  102. assert subsection[0].text == "const:"
  103. for ldesc in subsection[1:]:
  104. tokens = tokenize_const(ldesc.text)
  105. print(tokens)
  106. def main(argv):
  107. if len(argv) == 1:
  108. print("error: missing proof file", file=sys.stderr)
  109. return -1
  110. filename = sys.argv[1]
  111. text = open(filename, "r").read()
  112. if (linedescs := parse(text)) is None:
  113. return -1
  114. sections = section(linedescs)
  115. consts, funcs, contracts = classify(sections)
  116. read_consts(consts)
  117. if __name__ == "__main__":
  118. main(sys.argv)