From 3c288e916555baf0fbf9fe23c9b3fbc0f1bbf78e Mon Sep 17 00:00:00 2001 From: Kyler Date: Tue, 2 Dec 2025 21:38:27 -0700 Subject: [PATCH] worked on filling in the project --- SLS_Python/.gitignore | 3 + SLS_Python/MANIFEST.in | 2 + SLS_Python/pyproject.toml | 16 +- SLS_Python/requirements-dev.txt | 4 - SLS_Python/scripts/_version.py.in | 3 + SLS_Python/scripts/write_version.py | 39 +++++ SLS_Python/sls/build_hooks.py | 16 ++ SLS_Python/sls/builtin.py | 7 - SLS_Python/sls/cli.py | 29 ---- SLS_Python/sls/interpreter.py | 252 +++++++++++++++++++++++++++- SLS_Python/sls/lexer.py | 245 ++++++++++++++++++++++++--- SLS_Python/sls/meta.py | 43 +++++ SLS_Python/sls/repl.py | 99 ++++++++--- 13 files changed, 663 insertions(+), 95 deletions(-) create mode 100644 SLS_Python/MANIFEST.in delete mode 100644 SLS_Python/requirements-dev.txt create mode 100644 SLS_Python/scripts/_version.py.in create mode 100644 SLS_Python/scripts/write_version.py create mode 100644 SLS_Python/sls/build_hooks.py delete mode 100644 SLS_Python/sls/cli.py create mode 100644 SLS_Python/sls/meta.py diff --git a/SLS_Python/.gitignore b/SLS_Python/.gitignore index 670a936..39b71fb 100644 --- a/SLS_Python/.gitignore +++ b/SLS_Python/.gitignore @@ -1,2 +1,5 @@ __pycache__/ .venv/ +sls/_version.py +sls_python.egg-info/ +dist/ diff --git a/SLS_Python/MANIFEST.in b/SLS_Python/MANIFEST.in new file mode 100644 index 0000000..2fe37bd --- /dev/null +++ b/SLS_Python/MANIFEST.in @@ -0,0 +1,2 @@ +include scripts/*.py +include scripts/*.in \ No newline at end of file diff --git a/SLS_Python/pyproject.toml b/SLS_Python/pyproject.toml index 74cc7f9..4082f2d 100644 --- a/SLS_Python/pyproject.toml +++ b/SLS_Python/pyproject.toml @@ -1,18 +1,20 @@ +# Build this file using 'python -m build' (from 'build' package) [build-system] requires = ["setuptools>=61.0", "wheel"] -build-backend = "setuptools.build_meta" +build-backend = "sls.build_hooks" [project] -name = "sls-python" +name = "sls_python" version = "0.1.0" description = "Python reimplementation skeleton of the SLS C project" -authors = [ { name = "Your Name" } ] +authors = [ { name = "Kyler Olsen" } ] readme = "README.md" license = { text = "MIT" } requires-python = ">=3.10" -dependencies = [ - "click>=8.0" -] +dependencies = [] + +[tool.setuptools] +packages = ["sls"] [project.scripts] -sls = "sls.cli:main" +sls = "sls.__main__:main" diff --git a/SLS_Python/requirements-dev.txt b/SLS_Python/requirements-dev.txt deleted file mode 100644 index 726db5c..0000000 --- a/SLS_Python/requirements-dev.txt +++ /dev/null @@ -1,4 +0,0 @@ -pytest -click -mypy -black diff --git a/SLS_Python/scripts/_version.py.in b/SLS_Python/scripts/_version.py.in new file mode 100644 index 0000000..6a2b4f6 --- /dev/null +++ b/SLS_Python/scripts/_version.py.in @@ -0,0 +1,3 @@ +# Auto-generated during build +commit = "{commit}" +timestamp = "{timestamp}" diff --git a/SLS_Python/scripts/write_version.py b/SLS_Python/scripts/write_version.py new file mode 100644 index 0000000..300894c --- /dev/null +++ b/SLS_Python/scripts/write_version.py @@ -0,0 +1,39 @@ +import subprocess +import datetime +import pathlib + +root = pathlib.Path(__file__).resolve().parents[1] +template = root / "scripts" / "_version.py.in" +output = root / "sls" / "_version.py" + +def get_commit(): + try: + result_hash = subprocess.check_output( + ["git", "describe", "--always", "--dirty", "--abbrev=7"], + cwd=".", + stderr=subprocess.DEVNULL, + text=True + ).strip() + result_date = subprocess.check_output( + ["git", "show", "-s", "--format=%ci"], + cwd=".", + stderr=subprocess.DEVNULL, + text=True + ).strip() + return f"{result_hash} {result_date}" + except Exception: + return "unknown" + +def get_timestamp(): + return datetime.datetime.now(datetime.timezone.utc).isoformat() + "Z" + +def main(): + commit = get_commit() + timestamp = get_timestamp() + + text = template.read_text() + text = text.format(commit=commit, timestamp=timestamp) + output.write_text(text) + +if __name__ == "__main__": + main() diff --git a/SLS_Python/sls/build_hooks.py b/SLS_Python/sls/build_hooks.py new file mode 100644 index 0000000..4c984de --- /dev/null +++ b/SLS_Python/sls/build_hooks.py @@ -0,0 +1,16 @@ +import subprocess +from setuptools.build_meta import build_wheel as _build_wheel +from setuptools.build_meta import build_sdist as _build_sdist + +def _generate_version(): + subprocess.check_call(["python", "scripts/write_version.py"]) + + +def build_wheel(*args, **kwargs): + _generate_version() + return _build_wheel(*args, **kwargs) + + +def build_sdist(*args, **kwargs): + _generate_version() + return _build_sdist(*args, **kwargs) diff --git a/SLS_Python/sls/builtin.py b/SLS_Python/sls/builtin.py index 8be1bbe..e69de29 100644 --- a/SLS_Python/sls/builtin.py +++ b/SLS_Python/sls/builtin.py @@ -1,7 +0,0 @@ -"""Builtins and native functions for SLS.""" - -def load_builtins(): - """Return a dict of builtin names to callables.""" - return { - "print": print, - } diff --git a/SLS_Python/sls/cli.py b/SLS_Python/sls/cli.py deleted file mode 100644 index fd052d7..0000000 --- a/SLS_Python/sls/cli.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Command-line entry point for SLS Python.""" -import click - -from . import __version__ - - -@click.group() -@click.version_option(__version__) -def main(): - """sls: Small Lisp-like language (Python reimplementation).""" - pass - - -@main.command() -def repl(): - """Start a minimal REPL.""" - from .repl import repl_loop - - repl_loop() - - -@main.command() -@click.argument("file", required=False) -def run(file): - """Run an SLS source file (placeholder).""" - if not file: - click.echo("No file provided; start with `sls repl`") - return - click.echo(f"Would run: {file} (not implemented yet)") diff --git a/SLS_Python/sls/interpreter.py b/SLS_Python/sls/interpreter.py index 5b6c63a..c7eb0e1 100644 --- a/SLS_Python/sls/interpreter.py +++ b/SLS_Python/sls/interpreter.py @@ -1,12 +1,248 @@ -"""Interpreter module placeholder for SLS.""" +# sls/interpreter.py +# Kyler Olsen +# YREA SLS +# Interpreter (Python OOP port) +# November 2025 -from typing import Any +from __future__ import annotations +from dataclasses import dataclass +from enum import Enum, auto +from typing import Callable, Dict, List, Optional + +# import the types from your lexer module +# adjust the import path to where you put the lexer module +from .lexer import ( + Token, + TokenType, + TokenString, + IntegerLiteral, + IntegerBuiltInType, +) + +# Optional: import builtins loader if you have one +# from .builtin import load_builtins # (expected: Callable[[InterpreterState], bool]) -class Interpreter: - def __init__(self): - self.env = {} +class StackType(Enum): + IDENTIFIER = auto() + I64 = auto() + I32 = auto() + I16 = auto() + I8 = auto() + U64 = auto() + U32 = auto() + U16 = auto() + U8 = auto() + FLOAT = auto() + DOUBLE = auto() + CHARACTER = auto() + BOOLEAN = auto() + TOKEN_STRING = auto() + CALLABLE = auto() - def eval(self, ast: Any): - """Evaluate the AST and return a result. Not implemented yet.""" - return None + +@dataclass +class StackEntry: + type: StackType + value: object # Identifier | int | float | bool | TokenString | FunctionItem etc. + + +class FunctionType(Enum): + TOKEN_STRING = auto() + BUILTIN = auto() + + +@dataclass +class FunctionItem: + type: FunctionType + token_string: Optional[TokenString] = None + builtin: Optional[Callable[["InterpreterState"], bool]] = None + + @classmethod + def from_token_string(cls, ts: TokenString) -> "FunctionItem": + return cls(type=FunctionType.TOKEN_STRING, token_string=ts) + + @classmethod + def from_builtin(cls, fn: Callable[["InterpreterState"], bool]) -> "FunctionItem": + return cls(type=FunctionType.BUILTIN, builtin=fn) + + +class InterpreterState: + """ + Interpreter state holds: + - stack: list of StackEntry (top of stack is stack[-1]) + - functions: dict mapping names to FunctionItem + """ + + def __init__(self, load_builtins: Optional[Callable[["InterpreterState"], bool]] = None): + self.stack: List[StackEntry] = [] + self.functions: Dict[str, FunctionItem] = {} + # Optionally load builtins; caller can pass a loader function + if load_builtins is not None: + ok = load_builtins(self) + if not ok: + raise RuntimeError("Failed to load builtins") + + # ------------------------- + # stack helpers + # ------------------------- + def push(self, entry: StackEntry) -> None: + self.stack.append(entry) + + def pop(self) -> Optional[StackEntry]: + if not self.stack: + return None + return self.stack.pop() + + def top(self) -> Optional[StackEntry]: + if not self.stack: + return None + return self.stack[-1] + + # ------------------------- + # function table + # ------------------------- + def add_function(self, name: str, item: FunctionItem) -> None: + self.functions[name] = item + + def get_function(self, name: str) -> Optional[FunctionItem]: + return self.functions.get(name) + + # ------------------------- + # execution primitives + # ------------------------- + def push_token(self, token: Token) -> bool: + """ + Push a token onto the stack. Returns True on success, False on failure. + Mirrors the logic from the C implementation: + - disallow TOKEN_STRING, TOKEN_ARRAY, TOKEN_TYPE_TUPLE + - return True for TOKEN_EOF (no-op) + """ + ttype = token.type + + if ttype == TokenType.EOF: + return True + + # map token -> stack type + extract value + if ttype == TokenType.IDENTIFIER: + entry = StackEntry(StackType.IDENTIFIER, token.identifier) + self.push(entry) + return True + + if ttype == TokenType.INTEGER: + ilit: IntegerLiteral = token.integer_literal # type: ignore + itype = ilit.type + # choose stack type explicitly to mirror C widths + if itype == IntegerBuiltInType.I64: + st = StackType.I64 + val = int(ilit.value) + elif itype == IntegerBuiltInType.I32: + st = StackType.I32 + val = int(ilit.value) & 0xFFFFFFFF # mimic truncation if desired + elif itype == IntegerBuiltInType.I16: + st = StackType.I16 + val = int(ilit.value) & 0xFFFF + elif itype == IntegerBuiltInType.I8: + st = StackType.I8 + val = int(ilit.value) & 0xFF + elif itype == IntegerBuiltInType.U64: + st = StackType.U64 + val = int(ilit.value) + elif itype == IntegerBuiltInType.U32: + st = StackType.U32 + val = int(ilit.value) & 0xFFFFFFFF + elif itype == IntegerBuiltInType.U16: + st = StackType.U16 + val = int(ilit.value) & 0xFFFF + elif itype == IntegerBuiltInType.U8: + st = StackType.U8 + val = int(ilit.value) & 0xFF + else: + return False + + self.push(StackEntry(st, val)) + return True + + if ttype == TokenType.FLOAT: + self.push(StackEntry(StackType.FLOAT, token.float_literal)) + return True + + if ttype == TokenType.DOUBLE: + self.push(StackEntry(StackType.DOUBLE, token.double_literal)) + return True + + if ttype == TokenType.CHARACTER: + self.push(StackEntry(StackType.CHARACTER, token.character_literal)) + return True + + if ttype == TokenType.STRING: + # C returned FALSE for TOKEN_STRING + return False + + if ttype == TokenType.BOOLEAN: + self.push(StackEntry(StackType.BOOLEAN, token.boolean_literal)) + return True + + if ttype == TokenType.ARRAY: + # C returned FALSE for arrays + return False + + if ttype == TokenType.TOKEN_STRING: + # copy token string to mimic C copy semantics + ts_copy = token.token_string.deep_copy() # type: ignore + self.push(StackEntry(StackType.TOKEN_STRING, ts_copy)) + return True + + if ttype == TokenType.TYPE_TUPLE: + # C returned FALSE for type tuples + return False + + # unknown token type + return False + + def execute_func(self, key: str) -> bool: + """ + Look up a function by name and execute it. + Builtin functions are callables that accept InterpreterState and return bool. + Token-string functions are executed via execute_token_string. + """ + func = self.get_function(key) + if func is None: + return False + + if func.type == FunctionType.BUILTIN: + if func.builtin is None: + return False + return func.builtin(self) + + if func.type == FunctionType.TOKEN_STRING: + if func.token_string is None: + return False + return self.execute_token_string(func.token_string) + + return False + + def execute_token_string(self, token_string: TokenString) -> bool: + """ + Execute each token in a TokenString. + If token is an identifier and not `is_literal`, treat it as a function call. + Otherwise push token onto the stack. + """ + for tok in token_string.tokens: + if tok.type == TokenType.IDENTIFIER and tok.identifier is not None and not tok.identifier.is_literal: + rv = self.execute_func(tok.identifier.name) + else: + rv = self.push_token(tok) + if not rv: + return False + return True + + def execute(self, token: Token) -> bool: + """ + Execute a single Token (this corresponds to processing a single lexer result). + If the token is an un-literal identifier -> function call, otherwise push it. + """ + if token.type == TokenType.IDENTIFIER and token.identifier is not None and not token.identifier.is_literal: + return self.execute_func(token.identifier.name) + else: + return self.push_token(token) diff --git a/SLS_Python/sls/lexer.py b/SLS_Python/sls/lexer.py index 23efb90..0ddaef8 100644 --- a/SLS_Python/sls/lexer.py +++ b/SLS_Python/sls/lexer.py @@ -1,25 +1,232 @@ -"""Lexer module placeholder for SLS. - -Provide a Lexer class that will later be expanded to match the C implementation. -""" - -from dataclasses import dataclass -from typing import Iterator, NamedTuple +from __future__ import annotations +from dataclasses import dataclass, field +from enum import Enum, auto +from typing import List, Optional, Any -class Token(NamedTuple): - type: str - value: str - lineno: int - col: int +# ===================================================================== +# Basic Types +# ===================================================================== + +class LexerInfo: + filename: str + source_code: str + pos: int + column: int + line: int + + def __init__(self, filename: str = "", source_code: str = ""): + self.filename = filename + self.source_code = source_code + self.pos = 0 + self.column = 0 + self.line = 1 + + +# ===================================================================== +# Token Types +# ===================================================================== + +class TokenType(Enum): + EOF = auto() + IDENTIFIER = auto() + INTEGER = auto() + FLOAT = auto() + DOUBLE = auto() + CHARACTER = auto() + STRING = auto() + BOOLEAN = auto() + ARRAY = auto() + TOKEN_STRING = auto() + TYPE_TUPLE = auto() + + +# ===================================================================== +# Array Literal Types +# ===================================================================== + +class ArrayType(Enum): + IDENTIFIER = auto() + I64 = auto() + I32 = auto() + I16 = auto() + I8 = auto() + U64 = auto() + U32 = auto() + U16 = auto() + U8 = auto() + FLOAT = auto() + DOUBLE = auto() + CHARACTER = auto() + STRING = auto() + BOOLEAN = auto() + STRUCT_INLINE = auto() + + +# ===================================================================== +# Identifier +# ===================================================================== + +@dataclass +class Identifier: + name: str + is_literal: bool + + +# ===================================================================== +# Integer Literal Type +# ===================================================================== + +class IntegerBuiltInType(Enum): + I64 = auto() + I32 = auto() + I16 = auto() + I8 = auto() + U64 = auto() + U32 = auto() + U16 = auto() + U8 = auto() @dataclass -class Lexer: - source: str +class IntegerLiteral: + value: int # Python int is arbitrary precision + type: IntegerBuiltInType - def tokenize(self) -> Iterator[Token]: - """Yield tokens from `source`. Currently yields a single EOF token. - Replace with full logic when porting the C lexer. - """ - yield Token("EOF", "", 1, 0) + +# ===================================================================== +# TokenString, TypeTuple, StructInline +# ===================================================================== + +@dataclass +class TokenString: + tokens: List["Token"] = field(default_factory=list) + + def deep_copy(self) -> TokenString: + copied_tokens = [Token( + type=token.type, + identifier=token.identifier, + integer_literal=token.integer_literal, + float_literal=token.float_literal, + double_literal=token.double_literal, + character_literal=token.character_literal, + string_literal=token.string_literal, + boolean_literal=token.boolean_literal, + array_literal=token.array_literal.deep_copy() if token.array_literal else None, + token_string=token.token_string.deep_copy() if token.token_string else None, + type_tuple=token.type_tuple.deep_copy() if token.type_tuple else None + ) for token in self.tokens] + return TokenString(tokens=copied_tokens) + + +@dataclass +class TypeTuple: + input_identifiers: List[Identifier] = field(default_factory=list) + output_identifiers: List[Identifier] = field(default_factory=list) + + def deep_copy(self) -> TypeTuple: + copied_input_ids = [Identifier(name=id.name, is_literal=id.is_literal) for id in self.input_identifiers] + copied_output_ids = [Identifier(name=id.name, is_literal=id.is_literal) for id in self.output_identifiers] + return TypeTuple(input_identifiers=copied_input_ids, output_identifiers=copied_output_ids) + + +@dataclass +class StructInline: + values: List[Any] # Python can store anything + name: str + + +# ===================================================================== +# ArrayLiteral (replaces C unions with Optional lists) +# ===================================================================== + +@dataclass +class ArrayLiteral: + type: ArrayType + + identifiers: Optional[List[Identifier]] = None + integer_literals: Optional[List[int]] = None + float_literals: Optional[List[float]] = None + double_literals: Optional[List[float]] = None + character_literals: Optional[List[int]] = None + string_literals: Optional[List[str]] = None + boolean_literals: Optional[List[bool]] = None + token_strings: Optional[List[TokenString]] = None + type_tuples: Optional[List[TypeTuple]] = None + struct_inline: Optional[StructInline] = None + + shape: Optional[List[int]] = None + dimensions: int = 0 + + def deep_copy(self) -> ArrayLiteral: + copied_array = ArrayLiteral(type=self.type, dimensions=self.dimensions, shape=list(self.shape) if self.shape else None) + + if self.identifiers is not None: + copied_array.identifiers = [Identifier(name=id.name, is_literal=id.is_literal) for id in self.identifiers] + if self.integer_literals is not None: + copied_array.integer_literals = list(self.integer_literals) + if self.float_literals is not None: + copied_array.float_literals = list(self.float_literals) + if self.double_literals is not None: + copied_array.double_literals = list(self.double_literals) + if self.character_literals is not None: + copied_array.character_literals = list(self.character_literals) + if self.string_literals is not None: + copied_array.string_literals = list(self.string_literals) + if self.boolean_literals is not None: + copied_array.boolean_literals = list(self.boolean_literals) + if self.token_strings is not None: + copied_array.token_strings = [ts.deep_copy() for ts in self.token_strings] + if self.type_tuples is not None: + copied_array.type_tuples = [tt.deep_copy() for tt in self.type_tuples] + if self.struct_inline is not None: + copied_array.struct_inline = StructInline( + values=list(self.struct_inline.values), + name=self.struct_inline.name + ) + + return copied_array + + +# ===================================================================== +# Token (Python “union” via Optional fields) +# ===================================================================== + +@dataclass +class Token: + type: TokenType + + identifier: Optional[Identifier] = None + integer_literal: Optional[IntegerLiteral] = None + float_literal: Optional[float] = None + double_literal: Optional[float] = None + character_literal: Optional[int] = None + string_literal: Optional[str] = None + boolean_literal: Optional[bool] = None + array_literal: Optional[ArrayLiteral] = None + token_string: Optional[TokenString] = None + type_tuple: Optional[TypeTuple] = None + + +# ===================================================================== +# Lexer Token Result / Lexer Result +# ===================================================================== + +class SlsResultType(Enum): + RESULT = auto() + ERROR = auto() + + +@dataclass +class FileInfo: + filename: str + line: int + column: int + + +# ===================================================================== +# Function Stubs (to be implemented in Python version) +# ===================================================================== + +def lexical_analysis(lexer_info: LexerInfo) -> list[Token]: + return [] diff --git a/SLS_Python/sls/meta.py b/SLS_Python/sls/meta.py new file mode 100644 index 0000000..e9f01e9 --- /dev/null +++ b/SLS_Python/sls/meta.py @@ -0,0 +1,43 @@ +# Kyler Olsen +# YREA SLS +# Meta File (Python port) +# November 2025 + +import sys +from datetime import datetime, timezone +try: + from ._version import commit, timestamp # type: ignore +except ImportError: + try: + import subprocess + result_hash = subprocess.check_output( + ["git", "describe", "--always", "--dirty", "--abbrev=7"], + cwd=".", + stderr=subprocess.DEVNULL, + text=True + ).strip() + result_date = subprocess.check_output( + ["git", "show", "-s", "--format=%ci"], + cwd=".", + stderr=subprocess.DEVNULL, + text=True + ).strip() + commit = f"{result_hash} {result_date}" + timestamp = datetime.now(timezone.utc).isoformat() + "Z" + except Exception: + commit = "unknown" + timestamp = "unknown" + + +# Equivalent of SLS_NAME and SLS_VER +SLS_NAME = "SLS_PYTHON" +SLS_VER = "a.0.0" + +# Runtime interpreter info (Python equivalent of compiler) +_impl = sys.implementation +INTERPRETER_NAME = _impl.name.capitalize() +INTERPRETER_VER = _impl.version.major + +def print_version() -> None: + print(f"YREA SLS ({SLS_NAME}) {SLS_VER} ({commit})") + print(f"Running on {INTERPRETER_NAME} {INTERPRETER_VER} at {timestamp}") diff --git a/SLS_Python/sls/repl.py b/SLS_Python/sls/repl.py index 68b98c0..39fc85e 100644 --- a/SLS_Python/sls/repl.py +++ b/SLS_Python/sls/repl.py @@ -1,25 +1,82 @@ -"""Simple REPL for SLS skeleton.""" - +from .meta import print_version from .lexer import Lexer -from .parser import Parser -from .interpreter import Interpreter +from .interpreter import InterpreterState +from .errors import SlsError -def repl_loop(): - interp = Interpreter() - print("sls-python REPL (very minimal). Type 'quit' or Ctrl-D to exit.") - try: - while True: - src = input("sls> ") - if not src or src.strip() in ("quit", "exit"): +REPL_FILE_NAME = "" + + +def print_top_of_stack(interpreter: InterpreterState) -> None: + """Pretty-print top of the stack.""" + + item = interpreter.top() + if item is None: + print("#0: ") + return + + t = item.type + + if t == "Identifier": + print(f"#0: ::{item.value}") + elif t in {"i64", "i32", "i16", "i8"}: + print(f"#0: {item.value}:{t}") + elif t in {"u64", "u32", "u16", "u8"}: + print(f"#0: {item.value}:{t}") + elif t == "f32": + print(f"#0: {item.value}:f32") + elif t == "f64": + print(f"#0: {item.value}") + elif t == "char": + print(f"#0: {item.value}") + elif t == "bool": + print("#0: TRUE" if item.value else "#0: FALSE") + elif t == "TokenString": + print("#0: ") + elif t == "Callable": + print("#0: ") + else: + print(f"#0: ") + + +def repl() -> int: + """Interactive interpreter loop.""" + + print_version() + print("===== YREA SLS REPL =====") + print("Type `#exit` to exit.") + + interpreter = InterpreterState() + + while True: + try: + line = input("> ") + except EOFError: + break + + if line.strip() == "#exit": + return 0 + + # Create a fresh lexer each iteration + lexer = Lexer(REPL_FILE_NAME, line) + + try: + tokens = lexer.lexical_analysis() + except SlsError as err: + print(err.message) + continue + + # Execute tokens in order + for token in tokens: + try: + ok = interpreter.execute(token) + if not ok: + print("A runtime error occurred!") + break + except SlsError as err: + print(err.message) break - # minimal echo behaviour for now - lexer = Lexer(src) - tokens = list(lexer.tokenize()) - ast = Parser(tokens).parse() - result = interp.eval(ast) - if result is not None: - print(result) - except (EOFError, KeyboardInterrupt): - print() - print("Bye") + + print_top_of_stack(interpreter) + + return 1