feat(repl): add dedicated REPL class

This commit is contained in:
2026-02-08 02:05:49 +01:00
parent f849c6969b
commit d23875524e
3 changed files with 117 additions and 77 deletions

View File

@@ -4,6 +4,7 @@ import argparse
import os
from pathlib import Path
from src.repl import REPL
from src.runner import Runner
@@ -23,7 +24,7 @@ def main():
args = parser.parse_args()
if args.script is None:
Runner.repl()
REPL().run()
else:
Runner.run_file(args.script)

113
src/repl.py Normal file
View File

@@ -0,0 +1,113 @@
import atexit
import readline
from pathlib import Path
from src.ast.stmt import Stmt
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()
@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 run(self):
line: str
tokens: list[Token]
program: list[Stmt]
self.greet()
self.init_history()
buf: str = ""
while True:
try:
line = input(">>> " if len(buf) == 0 else "... ")
except EOFError:
print()
break
except KeyboardInterrupt:
buf = ""
print()
continue
buf += line + "\n"
try:
tokens = Lexer(buf).process()
except:
continue
if Pebble.had_error:
Pebble.had_error = False
continue
if self.unfinished_statement(tokens):
continue
program = Parser(tokens).parse()
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

View File

@@ -5,10 +5,10 @@ from src.ast.stmt import Stmt
from src.formatter import Formatter
from src.interpreter.interpreter import Interpreter
from src.interpreter.resolver import Resolver
from src.token.lexer import Lexer
from src.parser.parser import Parser
from src.pebble import Pebble
from src.token.token import Token, TokenType
from src.token.lexer import Lexer
from src.token.token import Token
class Runner:
@@ -55,77 +55,3 @@ class Runner:
formatter: Formatter = Formatter()
with open("formatted.peb", "w") as f:
f.write(formatter.print(program))
@staticmethod
def repl():
line: str
lexer: Lexer
tokens: list[Token]
parser: Parser
program: list[Stmt]
interpreter: Interpreter = Interpreter()
resolver: Resolver
print(f"Welcome to Pebble v{Pebble.version()}!")
print("Press CTRL+D to quit")
brace_depth: int
paren_depth: int
buf: str = ""
while True:
try:
line = input(">>> " if len(buf) == 0 else "... ")
except EOFError:
print()
break
except KeyboardInterrupt:
buf = ""
print()
continue
buf += line + "\n"
lexer = Lexer(buf)
tokens = lexer.process()
if Pebble.had_error:
Pebble.had_error = False
continue
brace_depth = 0
paren_depth = 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)
if brace_depth > 0 or paren_depth > 0:
continue
parser = Parser(tokens)
program = parser.parse()
buf = ""
if Pebble.had_error:
Pebble.had_error = False
continue
resolver: Resolver = Resolver(interpreter)
resolver.resolve(*program)
if Pebble.had_error:
Pebble.had_error = False
continue
try:
interpreter.interpret(program)
except KeyboardInterrupt:
print()
if Pebble.had_runtime_error:
Pebble.had_runtime_error = False
continue