feat(repl): add dedicated REPL class
This commit is contained in:
3
main.py
3
main.py
@@ -4,6 +4,7 @@ import argparse
|
|||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
from src.repl import REPL
|
||||||
from src.runner import Runner
|
from src.runner import Runner
|
||||||
|
|
||||||
|
|
||||||
@@ -23,7 +24,7 @@ def main():
|
|||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.script is None:
|
if args.script is None:
|
||||||
Runner.repl()
|
REPL().run()
|
||||||
else:
|
else:
|
||||||
Runner.run_file(args.script)
|
Runner.run_file(args.script)
|
||||||
|
|
||||||
|
|||||||
113
src/repl.py
Normal file
113
src/repl.py
Normal 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
|
||||||
@@ -5,10 +5,10 @@ from src.ast.stmt import Stmt
|
|||||||
from src.formatter import Formatter
|
from src.formatter import Formatter
|
||||||
from src.interpreter.interpreter import Interpreter
|
from src.interpreter.interpreter import Interpreter
|
||||||
from src.interpreter.resolver import Resolver
|
from src.interpreter.resolver import Resolver
|
||||||
from src.token.lexer import Lexer
|
|
||||||
from src.parser.parser import Parser
|
from src.parser.parser import Parser
|
||||||
from src.pebble import Pebble
|
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:
|
class Runner:
|
||||||
@@ -55,77 +55,3 @@ class Runner:
|
|||||||
formatter: Formatter = Formatter()
|
formatter: Formatter = Formatter()
|
||||||
with open("formatted.peb", "w") as f:
|
with open("formatted.peb", "w") as f:
|
||||||
f.write(formatter.print(program))
|
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
|
|
||||||
|
|||||||
Reference in New Issue
Block a user