Files
pebble/src/repl.py

122 lines
3.7 KiB
Python

import atexit
import readline
from pathlib import Path
from src.ast.stmt import Stmt
from src.completer.completer import Completer
from src.interpreter.interpreter import Interpreter
from src.interpreter.resolver import Resolver
from src.parser.parser import Parser
from src.pebble import Pebble
from src.token.lexer import Lexer
from src.token.token import Token, TokenType
class REPL:
HIST_FILE: Path = Path("~").expanduser() / ".pebble_history"
def __init__(self):
self.interpreter: Interpreter = Interpreter()
self.completer: Completer = Completer(self.interpreter)
self.buf: str = ""
@staticmethod
def greet():
print(f"Welcome to Pebble v{Pebble.version()}!")
print("Press CTRL+D to quit")
def init_history(self):
h_len: int = 0
try:
readline.read_history_file(self.HIST_FILE)
h_len = readline.get_current_history_length()
except FileNotFoundError:
with open(self.HIST_FILE, "wb"):
pass
def save(prev_h_len: int, hist_file: Path):
new_h_len: int = readline.get_current_history_length()
readline.set_history_length(1000)
readline.append_history_file(new_h_len - prev_h_len, hist_file)
atexit.register(save, h_len, self.HIST_FILE)
def init_completer(self):
readline.set_completer(self.completer.complete)
readline.parse_and_bind("tab: complete")
readline.set_completion_display_matches_hook(self.completer.display_matches)
def run(self):
line: str
tokens: list[Token]
program: list[Stmt]
self.greet()
self.init_history()
self.init_completer()
while True:
try:
line = input(">>> " if len(self.buf) == 0 else "... ")
except EOFError:
print()
break
except KeyboardInterrupt:
self.buf = ""
print()
continue
self.buf += line + "\n"
try:
tokens = Lexer(self.buf).process()
except:
continue
if Pebble.had_error:
Pebble.had_error = False
continue
if self.unfinished_statement(tokens):
continue
program = Parser(tokens).parse()
self.buf = ""
if Pebble.had_error:
Pebble.had_error = False
continue
Resolver(self.interpreter).resolve(*program)
if Pebble.had_error:
Pebble.had_error = False
continue
try:
self.interpreter.interpret(program)
except KeyboardInterrupt:
print()
if Pebble.had_runtime_error:
Pebble.had_runtime_error = False
continue
@staticmethod
def unfinished_statement(tokens: list[Token]) -> bool:
brace_depth: int = 0
paren_depth: int = 0
fstring_depth: int = 0
for token in tokens:
match token.type:
case TokenType.LEFT_BRACE:
brace_depth += 1
case TokenType.RIGHT_BRACE:
brace_depth = max(0, brace_depth - 1)
case TokenType.LEFT_PAREN:
paren_depth += 1
case TokenType.RIGHT_PAREN:
paren_depth = max(0, paren_depth - 1)
case TokenType.FSTRING_START:
fstring_depth += 1
case TokenType.FSTRING_END:
fstring_depth = max(0, fstring_depth - 1)
return brace_depth > 0 or paren_depth > 0 or fstring_depth > 0