Skip to content

Instantly share code, notes, and snippets.

@kayws426
Forked from roberthoenig/lisparser.py
Last active August 5, 2023 14:37
Show Gist options
  • Save kayws426/465542f793c53c800305d903f0e11bbf to your computer and use it in GitHub Desktop.
Save kayws426/465542f793c53c800305d903f0e11bbf to your computer and use it in GitHub Desktop.
Simple Lisp parser for Python 3.
import sys
from typing import Any, List
# Check input character is a token separator
def is_tok_sep(c: str) -> bool:
return c in "() \r\n\t\f"
# Parse input string into a list of all parentheses and atoms (int or str),
# exclude whitespaces.
def normalize_str(string: str) -> List[str]:
str_norm = []
last_c = None
in_quote = False
for c in string:
if c == '"':
in_quote = not in_quote
if in_quote:
str_norm.append('')
if in_quote:
str_norm[-1] += c
else:
if not is_tok_sep(c):
if not is_tok_sep(last_c):
str_norm[-1] += c
else:
str_norm.append(c)
elif not c.isspace():
str_norm.append(c)
last_c = c
return str_norm
# Generate abstract syntax tree from normalized input.
def get_ast(input_norm: List[str]) -> List[Any]:
ast = []
# Go through each element in the input:
# - if it is an open parenthesis, find matching parenthesis and make recursive
# call for content in-between. Add the result as an element to the current list.
# - if it is an atom, just add it to the current list.
i = 0
while i < len(input_norm):
symbol = input_norm[i]
if symbol == '(':
list_content = []
match_ctr = 1 # If 0, parenthesis has been matched.
while match_ctr != 0:
i += 1
if i >= len(input_norm):
raise ValueError("Invalid input: Unmatched open parenthesis.")
symbol = input_norm[i]
if symbol == '(':
match_ctr += 1
elif symbol == ')':
match_ctr -= 1
if match_ctr != 0:
list_content.append(symbol)
ast.append(get_ast(list_content))
elif symbol == ')':
raise ValueError("Invalid input: Unmatched close parenthesis.")
else:
ast.append(symbol)
i += 1
return ast
def main():
input_str = sys.stdin.read()
input_norm = normalize_str(input_str)
ast = get_ast(input_norm)
print(ast)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment