122 lines
3.7 KiB
Python
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
|