From 95b6ffaa76ed3cd210f6f327aebfc86d096b27ef Mon Sep 17 00:00:00 2001 From: Yiyun Liu Date: Wed, 6 May 2026 01:46:37 +0000 Subject: [PATCH] Port Autumn.wasm refactor from MARA monorepo Brings the standalone Autumn.cpp tree in line with the version vendored at MARA/MARA/domains/autumnbench/Autumn.wasm/. Largest change is the AST refactor: Expr/Stmt now use direct eval(Interpreter&)/exec(Interpreter&) methods instead of the visitor pattern, and identifiers are interned through Autumn::Interner (Symbol = const std::string*) for fast equality and stable iteration. Adds new src/interpreter/ExprMethods.cpp and include/interpreter/Interner.hpp. Build/packaging updates: - CMakeLists.txt: C++20, scikit-build-core/pybind11 wheel layout (installs to MARA/autumn_cpp/), in-tree .venv auto-detection - pyproject.toml: cibuildwheel matrix for cp312/313/314 across manylinux x86_64+aarch64 and macosx x86_64+arm64 - .github/workflows/build.yml: build_wheels (4-OS matrix), build_wasm (emcmake), tag-triggered release - README rewritten around uv + cmake + wheel install + WASM Cleanup of dead/redundant files: - include/static_analyzer/ (never used, broken visitor sketch) - testInterpreters/ (broken demos, never wired to ctest) - Makefile (subsumed by CMakeLists) - environment.yml, requirements.txt (subsumed by pyproject.toml) - python_test.py (arcade-only, can't run headless) New sexp programs from MARA were intentionally NOT brought over. --- .github/workflows/build.yml | 79 ++ .gitignore | 2 + CMakeLists.txt | 150 +++- Makefile | 129 --- README.md | 214 +++-- environment.yml | 7 - include/Expr.hpp | 264 +++--- include/Stmt.hpp | 53 +- include/interpreter/AutumnCallable.hpp | 1 - include/interpreter/AutumnStdLib.hpp | 258 +----- include/interpreter/Environment.hpp | 200 ++--- include/interpreter/Interner.hpp | 32 + include/interpreter/Interpreter.hpp | 136 ++- include/interpreter/VarCollector.hpp | 132 +-- include/interpreter/typesystem/AutumnType.hpp | 76 +- .../valuesystem/AutumnCallableValue.hpp | 60 +- .../interpreter/valuesystem/AutumnClass.hpp | 63 +- .../valuesystem/AutumnConstructor.hpp | 44 +- .../valuesystem/AutumnExprValue.hpp | 25 +- .../valuesystem/AutumnInstance.hpp | 152 ++-- .../interpreter/valuesystem/AutumnLambda.hpp | 48 +- .../interpreter/valuesystem/AutumnValue.hpp | 333 ++++--- include/parser/AstPrinter.hpp | 250 +----- include/parser/Parser.hpp | 67 +- include/static_analyzer/StaticAnalyzer.hpp | 81 -- install_scripts/setup_emscripten.sh | 37 +- pyproject.toml | 26 + python_pkg/__init__.py | 7 + python_pkg/autumnstdlib/__init__.py | 7 + python_test.py | 181 ---- requirements.txt | 2 - src/interpreter/AutumnStdComponents.cpp | 38 +- src/interpreter/Environment.cpp | 84 +- src/interpreter/ExprMethods.cpp | 219 +++++ src/interpreter/Interpreter.cpp | 809 ++++++------------ src/interpreter/stdlib/Any.cpp | 3 - src/interpreter/stdlib/Clicked.cpp | 16 +- src/interpreter/stdlib/Defined.cpp | 3 +- src/interpreter/stdlib/DownPressed.cpp | 9 +- src/interpreter/stdlib/IsOutsideBounds.cpp | 3 +- src/interpreter/stdlib/IsWithinBounds.cpp | 2 +- src/interpreter/stdlib/LeftPressed.cpp | 9 +- src/interpreter/stdlib/RenderAll.cpp | 2 +- src/interpreter/stdlib/RightPressed.cpp | 9 +- src/interpreter/stdlib/Rotate.cpp | 2 +- src/interpreter/stdlib/UpPressed.cpp | 9 +- src/interpreter/stdlib/isFreePos.cpp | 9 +- src/interpreter/typesystem/AutumnType.cpp | 6 +- src/interpreter/valuesystem/AutumnClass.cpp | 4 +- .../valuesystem/AutumnExprValue.cpp | 26 +- src/interpreter/valuesystem/AutumnValue.cpp | 248 ++++-- test2.jl | 4 +- testInterpreters/test_grow.cpp | 58 -- testInterpreters/test_light.cpp | 38 - tools/bindings.cpp | 12 +- tools/bindings_julia.cpp | 6 +- tools/bindings_python.cpp | 129 ++- tools/generate_ast.cpp | 5 +- tools/main.cpp | 9 +- tools/parser.cpp | 4 +- 60 files changed, 2063 insertions(+), 2828 deletions(-) create mode 100644 .github/workflows/build.yml delete mode 100644 Makefile delete mode 100644 environment.yml create mode 100644 include/interpreter/Interner.hpp delete mode 100644 include/static_analyzer/StaticAnalyzer.hpp mode change 100644 => 100755 install_scripts/setup_emscripten.sh create mode 100644 pyproject.toml create mode 100644 python_pkg/__init__.py create mode 100644 python_pkg/autumnstdlib/__init__.py delete mode 100644 python_test.py delete mode 100644 requirements.txt create mode 100644 src/interpreter/ExprMethods.cpp delete mode 100644 testInterpreters/test_grow.cpp delete mode 100644 testInterpreters/test_light.cpp diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..da568ce --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,79 @@ +name: Build + +on: + push: + branches: [main] + tags: ['v*'] + pull_request: + +jobs: + build_wheels: + name: Build wheels on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, ubuntu-24.04-arm, macos-26-intel, macos-latest] + + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + + - name: Build wheels + uses: pypa/cibuildwheel@v3.4.1 + with: + output-dir: wheelhouse + + - uses: actions/upload-artifact@v4 + with: + name: autumn-cpp-wheels-${{ matrix.os }} + path: ./wheelhouse/*.whl + + build_wasm: + name: Build wasm interpreter + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + + - uses: emscripten-core/setup-emsdk@v15 + + - name: Build Autumn.wasm + run: | + emcmake cmake -S . -B build-wasm -DCMAKE_BUILD_TYPE=Release + cmake --build build-wasm -j + + - name: Stage artifacts + run: | + mkdir autumn.wasm + cp build-wasm/interpreter_web.js \ + build-wasm/interpreter_web.wasm \ + autumnstdlib/stdlib.sexp \ + autumn.wasm/ + tar cJf autumn.wasm.tar.xz autumn.wasm + + - uses: actions/upload-artifact@v4 + with: + name: autumn-wasm-tarball + path: autumn.wasm.tar.xz + + release: + if: github.ref_type == 'tag' + needs: [build_wheels, build_wasm] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/download-artifact@v4 + with: + path: artifacts + merge-multiple: true + + - name: Release + uses: softprops/action-gh-release@v2 + with: + files: | + artifacts/*.whl + artifacts/autumn.wasm.tar.xz diff --git a/.gitignore b/.gitignore index 5b41260..a276240 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ build/ +build-wasm/ .cache/ +.venv/ stderr* stdout* std*err* diff --git a/CMakeLists.txt b/CMakeLists.txt index 575a816..198ad46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,60 +1,77 @@ -cmake_minimum_required(VERSION 3.5) -project(FunctionalInterpreter) +cmake_minimum_required(VERSION 3.18) +project(FunctionalInterpreter VERSION 1.0.0) + +# Force use of the specified Python3_EXECUTABLE if provided +if(DEFINED Python3_EXECUTABLE) + set(Python3_EXECUTABLE "${Python3_EXECUTABLE}" CACHE FILEPATH "Python3 executable" FORCE) + set(PYTHON_EXECUTABLE "${Python3_EXECUTABLE}" CACHE FILEPATH "Python executable" FORCE) + message(STATUS "Using explicitly provided Python: ${Python3_EXECUTABLE}") +endif() # Set C++ standard -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) +# Enable lto by default +set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + # Emit compile_commands.json for use with clangd set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -# If CXX contains clang, set -O3 -flto -fwhole-program-vtables -if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(contain_clang TRUE) -else() - set(contain_clang FALSE) -endif() - -if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") - set(contain_gcc TRUE) -endif() - -if(MSVC) # built-in shortcut for 'CMAKE_CXX_COMPILER_ID STREQUAL "MSVC"' - set(contain_msvc TRUE) -endif() -message("CMAKE_CXX_COMPILER_ID: ${CMAKE_CXX_COMPILER_ID}") -if(contain_clang) - message("Contain Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -flto -fwhole-program-vtables") -elseif(contain_gcc) - message("Contain GCC") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -flto \ - -fno-semantic-interposition -fvisibility=hidden -fvisibility-inlines-hidden") -elseif(contain_msvc) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O2 /flto /GL") +file(GLOB_RECURSE SOURCES "src/*.cpp") +include_directories(include include/interpreter include/lexer include/parser + include/interpreter/valuesystem include/interpreter/typesystem) + +# Emscripten / WebAssembly build +# Usage: +# emcmake cmake -S . -B build-wasm -DCMAKE_BUILD_TYPE=Release +# cmake --build build-wasm --target interpreter_web +if(EMSCRIPTEN) + message(STATUS "Building for Emscripten/WASM (build type: ${CMAKE_BUILD_TYPE})") + + add_executable(interpreter_web tools/bindings.cpp ${SOURCES}) + set_target_properties(interpreter_web PROPERTIES SUFFIX ".js") + + target_compile_options(interpreter_web PRIVATE + -fvisibility=hidden -fvisibility-inlines-hidden + -msimd128 + -fwasm-exceptions) + + target_link_options(interpreter_web PRIVATE + --bind + -fwasm-exceptions + "SHELL:-s EXPORT_ES6=1" + "SHELL:-s MODULARIZE=1" + "SHELL:-s EXPORT_NAME=createInterpreterModule" + "SHELL:-s ALLOW_MEMORY_GROWTH=1") + + # Skip all the native-only machinery below (pybind11, Julia, shell tools). + return() endif() option(AUTUMN_DEV_MODE "Enable development mode with debug prints" OFF) +option(AUTUMN_BUILD_PYTHON_MODULE "Build the pybind11 Python extension (interpreter_module)" ON) if(AUTUMN_DEV_MODE) add_definitions(-DAUTUMN_DEV_MODE) endif() -file(GLOB_RECURSE SOURCES "src/*.cpp") - -# Include directories -include_directories(include include/interpreter include/lexer include/parser include/interpreter/valuesystem include/interpreter/typesystem) +# SOURCES and include_directories are declared near the top of this file so +# both the WASM and native branches can share them. add_executable(sexp_parser tools/sexp_parser.cpp src/parser/sexpresso.cpp) add_executable(generate_ast tools/generate_ast.cpp) add_executable(lexer tools/lexer.cpp src/Token.cpp) add_executable(parser tools/parser.cpp src/Token.cpp src/parser/sexpresso.cpp) + add_library(AutumnLib ${SOURCES}) target_include_directories(AutumnLib PUBLIC ${PROJECT_SOURCE_DIR} ) +target_link_libraries(parser PRIVATE AutumnLib) + add_executable(TokenTypeTest test_suites/test_token_type.cpp ) @@ -64,20 +81,65 @@ target_link_libraries(TokenTypeTest PRIVATE AutumnLib) enable_testing() add_test(NAME TokenTypeTest COMMAND TokenTypeTest) -# Set python executable path -# Check if /opt/homebrew/bin/python exists -if(EXISTS "/opt/homebrew/bin/python") - set(PYTHON_EXECUTABLE "/opt/homebrew/bin/python") -endif() - message("SOURCES: ${SOURCES}") -# Create the executable +# Create the native CLI executable add_executable(interpreter tools/main.cpp ${SOURCES}) -find_package(pybind11 REQUIRED) +if(AUTUMN_BUILD_PYTHON_MODULE) + # Set python executable path + # If Python3_EXECUTABLE was passed to CMake, use it exclusively + if(NOT DEFINED Python3_EXECUTABLE OR NOT Python3_EXECUTABLE) + # Prefer in-tree venv at /.venv, then Homebrew, then system Python + set(VENV_PYTHON "${CMAKE_SOURCE_DIR}/.venv/bin/python") + + if(EXISTS "${VENV_PYTHON}") + set(Python3_EXECUTABLE "${VENV_PYTHON}" CACHE FILEPATH "Python3 executable" FORCE) + message(STATUS "Auto-detected venv Python: ${Python3_EXECUTABLE}") + elseif(EXISTS "/opt/homebrew/bin/python") + set(Python3_EXECUTABLE "/opt/homebrew/bin/python" CACHE FILEPATH "Python3 executable" FORCE) + message(STATUS "Auto-detected Homebrew Python: ${Python3_EXECUTABLE}") + else() + # Fall back to system Python + find_program(Python3_EXECUTABLE python3 python) + if(Python3_EXECUTABLE) + message(STATUS "Auto-detected system Python: ${Python3_EXECUTABLE}") + endif() + endif() + endif() -# Create the Python module -pybind11_add_module(interpreter_module tools/bindings_python.cpp ${SOURCES}) + # Set PYTHON_EXECUTABLE for backwards compatibility + set(PYTHON_EXECUTABLE "${Python3_EXECUTABLE}") + message(STATUS "Final Python for build: ${Python3_EXECUTABLE}") + + # Find Python3 FIRST with our specified executable + # This locks in the Python version before pybind11 tries to find it + find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Module) + message(STATUS "Locked Python3: ${Python3_EXECUTABLE} (version ${Python3_VERSION})") + + # Find pybind11 - it will now use the Python3 we just locked in + find_package(pybind11 REQUIRED) + + # Create the Python module + pybind11_add_module(interpreter_module tools/bindings_python.cpp ${SOURCES}) + + install( + TARGETS interpreter_module + LIBRARY DESTINATION MARA/autumn_cpp + COMPONENT python_extension + ) + + install( + FILES python_pkg/__init__.py + DESTINATION MARA/autumn_cpp + COMPONENT python_extension + ) + + install( + FILES python_pkg/autumnstdlib/__init__.py autumnstdlib/stdlib.sexp + DESTINATION MARA/autumn_cpp/autumnstdlib + COMPONENT python_extension + ) +endif() # Try to find Julia find_package(Julia QUIET) @@ -97,7 +159,7 @@ if(NOT Julia_FOUND) COMMAND ${Julia_EXECUTABLE} --startup-file=no -e "print(joinpath(Sys.BINDIR, \"../include/julia\"))" OUTPUT_VARIABLE Julia_INCLUDE_DIRS ) - + find_library(Julia_LIBRARY NAMES julia libjulia PATHS ${Julia_LIBRARY_DIR} @@ -115,7 +177,7 @@ endif() if(Julia_FOUND) message(STATUS "Found Julia: Building Julia bindings") find_package(JlCxx QUIET) - + if(JlCxx_FOUND) # Get Julia package directory execute_process( @@ -209,4 +271,4 @@ end endif() else() message(STATUS "Julia not found: Skipping Julia bindings") -endif() \ No newline at end of file +endif() diff --git a/Makefile b/Makefile deleted file mode 100644 index 4795b79..0000000 --- a/Makefile +++ /dev/null @@ -1,129 +0,0 @@ -############################################################################### -# Native build (g++) -############################################################################### -CXX := g++ -CXXFLAGS := -std=c++17 \ - -Iinclude -Iinclude/interpreter -Iinclude/lexer \ - -Iinclude/parser -Iinclude/interpreter/valuesystem \ - -Iinclude/interpreter/typesystem \ - -O3 -flto \ - -fwhole-program \ - -fno-semantic-interposition \ - -fvisibility=hidden \ - -Wall -Wextra \ - -DAUTUMN_DEV_MODE - -############################################################################### -# WebAssembly build (Emscripten / Clang) -############################################################################### -EMCC := em++ -EMCXXFLAGS := -std=c++17 \ - -Iinclude -Iinclude/interpreter -Iinclude/lexer \ - -Iinclude/parser -Iinclude/interpreter/valuesystem \ - -Iinclude/interpreter/typesystem \ - -O3 -flto \ - -s EXPORT_ES6=1 \ - -fwhole-program-vtables \ - -fvisibility=hidden -fvisibility-inlines-hidden \ - -s WASM=1 \ - -s ALLOW_TABLE_GROWTH=1 -s ALLOW_MEMORY_GROWTH=1 \ - --bind \ - -s DYNAMIC_EXECUTION=0 \ - -s ASSERTIONS=1 \ - -msimd128 \ - -s NO_DISABLE_EXCEPTION_CATCHING \ - -gsource-map -s LOAD_SOURCE_MAP=0 \ - -s INITIAL_MEMORY=10MB -s MAXIMUM_MEMORY=128MB \ - -DAUTUMN_DEV_MODE - -EMLINKFLAGS := -flto \ - -s ALLOW_MEMORY_GROWTH=1 \ - -s MODULARIZE=1 \ - -s 'EXPORT_NAME="createInterpreterModule"' \ - -s ASSERTIONS=1 \ - --bind \ - -gsource-map -s LOAD_SOURCE_MAP=0 \ - -msimd128 \ - -s NO_DISABLE_EXCEPTION_CATCHING \ - -s INITIAL_MEMORY=10MB -s MAXIMUM_MEMORY=128MB - -# TO enable exception handling in emscripten -# -s DISABLE_EXCEPTION_CATCHING=0 in both emcc and emlink - -# Source directories -SRC_DIR := src -TOOLS_DIR := tools -TEST_DIR := testInterpreters -INCLUDE_DIRS := -Iinclude -Iinclude/interpreter -Iinclude/lexer \ - -Iinclude/parser -Iinclude/interpreter/valuesystem \ - -Iinclude/interpreter/typesystem - -# Gather all source files -SOURCES := $(shell find $(SRC_DIR) -name '*.cpp') -INCLUDES := $(shell find include -type d | sed 's/^/-I/') - -# Targets -TARGETS := sexp_parser generate_ast lexer parser \ - test_light test_grow interpreter - -# Emscripten target -WEB_TARGET := interpreter_web.js -TS_DEF_TARGET := interpreter_web.d.ts - -# Output directories -DIST_DIR := dist - -# Default target -all: native - -.PHONY: native web clean dev web-dev - -# Native build -native: $(TARGETS) - -# Emscripten build -web: $(WEB_TARGET) - -# Build rules for native executables -sexp_parser: $(TOOLS_DIR)/sexp_parser.cpp $(SRC_DIR)/parser/sexpresso.cpp $(SOURCES) - $(CXX) $(CXXFLAGS) -o $@ $(TOOLS_DIR)/sexp_parser.cpp $(SRC_DIR)/parser/sexpresso.cpp - -generate_ast: $(TOOLS_DIR)/generate_ast.cpp - $(CXX) $(CXXFLAGS) -o $@ $(TOOLS_DIR)/generate_ast.cpp - -lexer: $(TOOLS_DIR)/lexer.cpp $(SRC_DIR)/Token.cpp - $(CXX) $(CXXFLAGS) -o $@ $(TOOLS_DIR)/lexer.cpp $(SRC_DIR)/Token.cpp - -parser: $(TOOLS_DIR)/parser.cpp $(SRC_DIR)/Token.cpp $(SRC_DIR)/parser/sexpresso.cpp - $(CXX) $(CXXFLAGS) -o $@ $(TOOLS_DIR)/parser.cpp $(SRC_DIR)/Token.cpp $(SRC_DIR)/parser/sexpresso.cpp - -test_light: $(TEST_DIR)/test_light.cpp $(SOURCES) - $(CXX) $(CXXFLAGS) -o $@ $(TEST_DIR)/test_light.cpp $(SOURCES) - -test_grow: $(TEST_DIR)/test_grow.cpp $(SOURCES) - $(CXX) $(CXXFLAGS) -o $@ $(TEST_DIR)/test_grow.cpp $(SOURCES) - -interpreter: $(TOOLS_DIR)/main.cpp $(SOURCES) - @mkdir -p $(DIST_DIR) - $(CXX) $(CXXFLAGS) -o $(DIST_DIR)/$@ $(TOOLS_DIR)/main.cpp $(SOURCES) - -# Build rule for Emscripten targets - one command generates both .js and .d.ts -$(WEB_TARGET) $(TS_DEF_TARGET): $(TOOLS_DIR)/bindings.cpp $(SOURCES) - $(EMCC) $(EMCXXFLAGS) -o $(WEB_TARGET) $(TOOLS_DIR)/bindings.cpp $(SOURCES) $(EMLINKFLAGS) --emit-tsd=$(TS_DEF_TARGET) - -# Clean build artifacts -clean: - rm -f $(TARGETS) $(WEB_TARGET) $(TS_DEF_TARGET) - rm -rf $(DIST_DIR) - -# Utility to print sources (optional) -print-sources: - @echo "SOURCES: $(SOURCES)" - -# Development build -dev: CXXFLAGS += $(DEV_FLAGS) -dev: native - -# Development web build -web-dev: EMCXXFLAGS += $(EMDEV_FLAGS) -web-dev: web \ No newline at end of file diff --git a/README.md b/README.md index 719fc0a..020a8dd 100644 --- a/README.md +++ b/README.md @@ -1,141 +1,165 @@ -# Autumn.cpp (a.k.a. Autumn.Wasm) A CPP Implementation of Autumn that compiles to WASM (and more) +# Autumn.cpp — A C++ Implementation of Autumn that compiles to WASM (and more) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -This repository hosts the Autumn interpreter, built with C++. It compiles to libraries usable in WebAssembly (`WASM`), `C++`, `Python` and `Julia`. +C++ implementation of the [Autumn](https://doi.org/10.1145/3571249) DSL interpreter. Builds to a native binary, a Python extension module (`mara-autumn-cpp` wheel via pybind11), a WebAssembly bundle, and a Julia binding. ![Example Programs: Particles, Magnets, and Space Invader](assets/examples_small.gif) -## Setting Up the Environment -Create a Conda environment using the following command: +## Using a pre-built release + +Pre-built wheels and the WASM bundle are published per release at [BasisResearch/Autumn.cpp/releases](https://github.com/BasisResearch/Autumn.cpp/releases). To install the wheel: + ```sh -conda env create --file environment.yml +pip install mara-autumn-cpp +# or, from a downloaded wheel: +pip install ./mara_autumn_cpp-*.whl ``` -## Using the pre-built package -To avoid the hassle in re-building, please try visiting the [Release](https://github.com/BasisResearch/Autumn.cpp/releases) page. Download the corresponding version and use it according to the [Wiki](https://github.com/BasisResearch/Autumn.cpp/wiki) - -## Building the C++ Interpreter -In general, install CMake, and as well as `requirements.txt`: -```shell -# on Mac: -brew install cmake -# on Linux -apt install cmake -# on both: -pip install -r requirements.txt -``` -Then build the python interpreter: -```shell -mkdir build && cd build -cmake .. -make -j20 -``` +After install, the extension is importable as `MARA.autumn_cpp.interpreter_module` (the package layout matches the `MARA` monorepo's expectations, but the wheel does not depend on MARA itself). + +## Building from source + +### Prerequisites + +- A C++20 compiler (clang or gcc) +- CMake ≥ 3.18 +- Python ≥ 3.12 with development headers (use [`uv`](https://docs.astral.sh/uv/) to manage Python — see below) +- For the WASM target: [Emscripten](https://emscripten.org/) (a helper script is included) + +### Quick start with `cmake` + +This builds just the native CLI + the C++ unit test — no Python toolchain needed. To also build the pybind11 Python extension, drop `-DAUTUMN_BUILD_PYTHON_MODULE=OFF` (it defaults to `ON`). -### Install Julia Package -Run julia and install CxxWrap ```sh -using Pkg -Pkg.add("CxxWrap") +# Configure +cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DAUTUMN_BUILD_PYTHON_MODULE=OFF + +# Build +cmake --build build -j + +# Run the test suite +ctest --test-dir build --output-on-failure ``` +This produces: + +| Artifact | Path | +|---|---| +| Native CLI interpreter | `build/interpreter` | +| Static C++ library | `build/libAutumnLib.a` | +| Token-type unit test | `build/TokenTypeTest` | +| Python extension module (when `AUTUMN_BUILD_PYTHON_MODULE=ON`) | `build/interpreter_module*.so` | + +### Building a wheel with `uv` -Compile the C++ interpreter by running: ```sh -mkdir build && cd build -cmake .. -DCMAKE_PREFIX_PATH=$(julia -e 'using CxxWrap;print(joinpath(CxxWrap.prefix_path()), "/lib/cmake")') -make -j12 -cd .. +uv build +# wheel lands in dist/ ``` -If you’re using macOS with Apple Silicon, use `cmake .. -DCMAKE_OSX_ARCHITECTURES=arm64` in place of `cmake ..` to ensures that all files are compiled and linked for arm64 architecture. +For multi-platform release wheels (manylinux x86_64/aarch64, macOS x86_64/arm64), the CI uses [`cibuildwheel`](https://cibuildwheel.pypa.io/) — see `.github/workflows/build.yml`. +### Building the WASM bundle +```sh +sh install_scripts/setup_emscripten.sh +source ./emsdk/emsdk_env.sh -## Running Autumn programs -### Python -Then, in the same folder, execute the following commands: +# Use the emscripten cmake wrapper to configure +emcmake cmake -B build-wasm -S . -DCMAKE_BUILD_TYPE=Release -```sh -cp build/*.so . -python python_test_mpl.py tests/grow.sexp 2> stderr_python.txt +# Once configured, build the project like you normally would +cmake --build build-wasm -j ``` -If you want to visualize preset of animation, first, install `ffmpeq` +Outputs `build-wasm/interpreter_web.js` and `build-wasm/interpreter_web.wasm`. + +### Building the Julia bindings + ```sh -# Mac -brew install ffmpeg -# Linux -apt install ffmpeg libffmpeg +julia -e 'using Pkg; Pkg.add("CxxWrap")' +cmake -B build -S . -DCMAKE_PREFIX_PATH=$(julia -e 'using CxxWrap; print(joinpath(CxxWrap.prefix_path(), "lib/cmake"))') +cmake --build build -j ``` +If the Julia toolchain is detected at configure time, an `AutumnInterpreter` package is staged into Julia's depot. + +## Running Autumn programs + +### From Python + +After building (no wheel install required), a smoke-test runner: -### Julia Build -In your julia project: ```sh -using Pkg; -Pkg.develop(path=joinpath(first(DEPOT_PATH), "packages", "AutumnInterpreter")) -Pkg.add("Plots") # This is for visualization -Pkg.add("PlotlyJS") # This is for visualization -Pkg.add("JSON3") # This is for visualization -Pkg.add("WebIO") -Pkg.add("Observables") -Pkg.add("JSExpr") -using AutumnInterpreter +PYTHONPATH=build .venv/bin/python python_test_mpl_ci.py tests/grow.sexp ``` -Finally, try out these tests: -```shell -julia test.jl -julia test2.jl +For an interactive matplotlib viewer (accepts `step`, `click x y`, `left`/`right`/`up`/`down`, `q` on stdin): + +```sh +.venv/bin/pip install matplotlib # or: pip install -e '.[viz]' +PYTHONPATH=build .venv/bin/python python_test_mpl.py tests/grow.sexp ``` -The main purpose is to allow building Autumn agent in Julia. For interactive purpose, please use Python package. +Additional matplotlib viewers (drag-and-drop / multiple-choice / animation modes) live in `extras/`. + +### From the native CLI -### WASM Build ```sh -sh install_scripts/setup_emscripten.sh -source ./emsdk/emsdk_env.sh -make web -cp interpreter_web.* ./flask_server/static/ +./build/interpreter tests/grow.sexp ``` -# What's working? -[BBQ](tests/bbq.sexp) [Egg](tests/egg.sexp) [Gravity 3](tests/gravity_3.sexp) [Grow](tests/grow.sexp) [Magnets](tests/magnets.sexp) [Paint](tests/paint.sexp) [Particles](tests/particles.sexp) [Sokoban II](tests/sokoban_ii.sexp) [Waterplug](tests/waterplug.sexp) [Game of Life](tests/gameOfLife.sexp) [Gravity 4](tests/gravity_4.sexp) [Ice](tests/ice.sexp) [Mario](tests/mario.sexp) [Particle 1](tests/particle_1.sexp) [Sand](tests/sand.sexp) [Sokoban](tests/sokoban.sexp) [Wind](tests/wind.sexp) +### From Julia +```julia +using Pkg +Pkg.develop(path=joinpath(first(DEPOT_PATH), "packages", "AutumnInterpreter")) +using AutumnInterpreter +``` + +See `test.jl` and `test2.jl` for example sessions. + +## Repository layout -# Bleeding Edge -We welcome and hope to receive contributions in the following modules: -- `python_test_mpl_ci.py` This is used for headless CI testing. -- `python_test` is a faster interface under development, but is not working properly, use `python_test_mpl.py` instead. +``` +src/ — interpreter sources +include/ — public headers (Expr, Stmt, Interpreter, Interner, …) +tools/ — entry points: native CLI, pybind11 bindings, + Julia bindings, WASM bindings, AST generator +autumnstdlib/ — Autumn standard library (stdlib.sexp) +tests/ — Autumn programs (.sexp) used as smoke / regression inputs +test_suites/ — C++ unit tests (registered with ctest) +extras/ — alternative matplotlib viewers +python_pkg/ — Python package layout used by `cmake --install` +.github/workflows/ — CI: wheel matrix + WASM build + tagged release +``` +## Citation -# Citations ```bibtex @article{das2023autumn, -author = {Das, Ria and Tenenbaum, Joshua B. and Solar-Lezama, Armando and Tavares, Zenna}, -title = {Combining Functional and Automata Synthesis to Discover Causal Reactive Programs}, -year = {2023}, -issue_date = {January 2023}, -publisher = {Association for Computing Machinery}, -address = {New York, NY, USA}, -volume = {7}, -number = {POPL}, -url = {https://doi.org/10.1145/3571249}, -doi = {10.1145/3571249}, -journal = {Proc. ACM Program. Lang.}, -month = jan, -articleno = {56}, -numpages = {31}, -keywords = {synthesis, reactive, causal, automata} + author = {Das, Ria and Tenenbaum, Joshua B. and Solar-Lezama, Armando and Tavares, Zenna}, + title = {Combining Functional and Automata Synthesis to Discover Causal Reactive Programs}, + year = {2023}, + publisher = {Association for Computing Machinery}, + journal = {Proc. ACM Program. Lang.}, + volume = {7}, + number = {POPL}, + articleno = {56}, + numpages = {31}, + doi = {10.1145/3571249}, + keywords = {synthesis, reactive, causal, automata} } ``` -# License -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. +## Acknowledgement + +The s-expression parser is built on [Sexpresso](https://github.com/BitPuffin/sexpresso). + +## License + +MIT — see [LICENSE](LICENSE). -# Acknowledgement -We acknowledge the open-source projects and communities that made this interpreter possible: -- The parser is made possible thanks to [Sexpresso - an S-Expression parser](https://github.com/BitPuffin/sexpresso) +## Contributors -# Contributors -Dat Nguyen, Archana Warrier, Yichao Liang, Cambridge Yang, Michelangelo Naim, Zenna Tarvares +Dat Nguyen, Archana Warrier, Yichao Liang, Cambridge Yang, Michelangelo Naim, Yiyun Liu, Zenna Tavares. diff --git a/environment.yml b/environment.yml deleted file mode 100644 index 592ba68..0000000 --- a/environment.yml +++ /dev/null @@ -1,7 +0,0 @@ -name: autumnwasm -channels: - - conda-forge -dependencies: - - python - - pybind11 - - matplotlib diff --git a/include/Expr.hpp b/include/Expr.hpp index 4e315ae..54466ec 100644 --- a/include/Expr.hpp +++ b/include/Expr.hpp @@ -1,288 +1,284 @@ #ifndef EXPR_H #define EXPR_H +#include "Interner.hpp" #include "Token.hpp" -#include #include #include #include namespace Autumn { -class Assign; -class Binary; -class Call; -class Get; -class Grouping; -class Literal; -class Logical; -class Set; -class Unary; -class Lambda; -class Variable; -class TypeVariable; -class TypeDecl; -class ListTypeExpr; -class ListVarExpr; -class IfExpr; -class Let; -class InitNext; +class AutumnValue; +class Interpreter; +class AutumnType; class Expr { public: - // Visitor interface - class Visitor { - public: - virtual std::any visitAssignExpr(std::shared_ptr stmt) = 0; - virtual std::any visitBinaryExpr(std::shared_ptr stmt) = 0; - virtual std::any visitCallExpr(std::shared_ptr stmt) = 0; - virtual std::any visitGetExpr(std::shared_ptr stmt) = 0; - virtual std::any visitGroupingExpr(std::shared_ptr stmt) = 0; - virtual std::any visitLiteralExpr(std::shared_ptr stmt) = 0; - virtual std::any visitLogicalExpr(std::shared_ptr stmt) = 0; - virtual std::any visitSetExpr(std::shared_ptr stmt) = 0; - virtual std::any visitUnaryExpr(std::shared_ptr stmt) = 0; - virtual std::any visitLambdaExpr(std::shared_ptr stmt) = 0; - virtual std::any visitVariableExpr(std::shared_ptr stmt) = 0; - virtual std::any - visitTypeVariableExpr(std::shared_ptr stmt) = 0; - virtual std::any visitTypeDeclExpr(std::shared_ptr stmt) = 0; - virtual std::any - visitListTypeExprExpr(std::shared_ptr stmt) = 0; - virtual std::any - visitListVarExprExpr(std::shared_ptr stmt) = 0; - virtual std::any visitIfExprExpr(std::shared_ptr stmt) = 0; - virtual std::any visitLetExpr(std::shared_ptr stmt) = 0; - virtual std::any visitInitNextExpr(std::shared_ptr stmt) = 0; - virtual ~Visitor() = default; - }; - - virtual std::any accept(Visitor &visitor) = 0; + virtual std::shared_ptr eval(Interpreter &interp) = 0; + virtual std::string prettyPrint() = 0; + virtual std::vector collectVars() = 0; + virtual std::shared_ptr resolveTypeExpr(Interpreter &interp); virtual ~Expr() = default; }; -class Assign : public Expr, public std::enable_shared_from_this { +class Assign : public Expr { public: - Assign(Token name, std::shared_ptr value) : name(name), value(value) {} + Assign(Token name, Symbol nameId, std::shared_ptr value) + : name(name), nameId(nameId), value(value) {} - std::any accept(Visitor &visitor) override { - return visitor.visitAssignExpr(shared_from_this()); - } + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override; const Token name; + const Symbol nameId; const std::shared_ptr value; }; -class Binary : public Expr, public std::enable_shared_from_this { +class Binary : public Expr { public: Binary(std::shared_ptr left, Token op, std::shared_ptr right) : left(left), op(op), right(right) {} - std::any accept(Visitor &visitor) override { - return visitor.visitBinaryExpr(shared_from_this()); - } + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override; const std::shared_ptr left; const Token op; const std::shared_ptr right; }; -class Call : public Expr, public std::enable_shared_from_this { +class Call : public Expr { public: Call(std::shared_ptr callee, std::vector> arguments) : callee(callee), arguments(arguments) {} - std::any accept(Visitor &visitor) override { - return visitor.visitCallExpr(shared_from_this()); - } + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override; const std::shared_ptr callee; const std::vector> arguments; }; -class Get : public Expr, public std::enable_shared_from_this { +class Get : public Expr { public: - Get(std::shared_ptr object, Token name) : object(object), name(name) {} + Get(std::shared_ptr object, Token name, Symbol nameId) + : object(object), name(name), nameId(nameId) {} - std::any accept(Visitor &visitor) override { - return visitor.visitGetExpr(shared_from_this()); - } + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override; const std::shared_ptr object; const Token name; + const Symbol nameId; }; -class Grouping : public Expr, public std::enable_shared_from_this { +class Grouping : public Expr { public: Grouping(std::shared_ptr expression) : expression(expression) {} - std::any accept(Visitor &visitor) override { - return visitor.visitGroupingExpr(shared_from_this()); - } + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override; const std::shared_ptr expression; }; -class Literal : public Expr, public std::enable_shared_from_this { +class IntLiteral : public Expr { public: - Literal(std::any value) : value(value) {} + explicit IntLiteral(int value) : value(value) {} - std::any accept(Visitor &visitor) override { - return visitor.visitLiteralExpr(shared_from_this()); - } + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override { return {}; } - const std::any value; + const int value; }; -class Logical : public Expr, public std::enable_shared_from_this { +class BoolLiteral : public Expr { +public: + explicit BoolLiteral(bool value) : value(value) {} + + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override { return {}; } + + const bool value; +}; + +class StringLiteral : public Expr { +public: + explicit StringLiteral(std::string value) : value(std::move(value)) {} + + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override { return {}; } + + const std::string value; +}; + +class Logical : public Expr { public: Logical(std::shared_ptr left, Token op, std::shared_ptr right) : left(left), op(op), right(right) {} - std::any accept(Visitor &visitor) override { - return visitor.visitLogicalExpr(shared_from_this()); - } + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override; const std::shared_ptr left; const Token op; const std::shared_ptr right; }; -class Set : public Expr, public std::enable_shared_from_this { +class Set : public Expr { public: - Set(std::shared_ptr object, Token name, std::shared_ptr value) - : object(object), name(name), value(value) {} + Set(std::shared_ptr object, Token name, Symbol nameId, + std::shared_ptr value) + : object(object), name(name), nameId(nameId), value(value) {} - std::any accept(Visitor &visitor) override { - return visitor.visitSetExpr(shared_from_this()); - } + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override; const std::shared_ptr object; const Token name; + const Symbol nameId; const std::shared_ptr value; }; -class Unary : public Expr, public std::enable_shared_from_this { +class Unary : public Expr { public: Unary(Token op, std::shared_ptr right) : op(op), right(right) {} - std::any accept(Visitor &visitor) override { - return visitor.visitUnaryExpr(shared_from_this()); - } + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override; const Token op; const std::shared_ptr right; }; -class Lambda : public Expr, public std::enable_shared_from_this { +class Lambda : public Expr { public: - Lambda(std::vector params, std::shared_ptr right) - : params(params), right(right) {} + Lambda(std::vector params, std::vector paramIds, + std::shared_ptr right) + : params(params), paramIds(paramIds), right(right) {} - std::any accept(Visitor &visitor) override { - return visitor.visitLambdaExpr(shared_from_this()); - } + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override; const std::vector params; + const std::vector paramIds; const std::shared_ptr right; }; -class Variable : public Expr, public std::enable_shared_from_this { +class Variable : public Expr { public: - Variable(Token name) : name(name) {} + Variable(Token name, Symbol nameId) : name(name), nameId(nameId) {} - std::any accept(Visitor &visitor) override { - return visitor.visitVariableExpr(shared_from_this()); - } + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override; const Token name; + const Symbol nameId; }; -class TypeVariable : public Expr, - public std::enable_shared_from_this { +class TypeVariable : public Expr { public: - TypeVariable(Token name) : name(name) {} + TypeVariable(Token name, Symbol nameId) : name(name), nameId(nameId) {} - std::any accept(Visitor &visitor) override { - return visitor.visitTypeVariableExpr(shared_from_this()); - } + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override { return {}; } + std::shared_ptr resolveTypeExpr(Interpreter &interp) override; const Token name; + const Symbol nameId; }; -class TypeDecl : public Expr, public std::enable_shared_from_this { +class AutumnType; // forward decl for evalAsFieldDecl + +class TypeDecl : public Expr { public: - TypeDecl(Token name, std::shared_ptr typeexpr) - : name(name), typeexpr(typeexpr) {} + TypeDecl(Token name, Symbol nameId, std::shared_ptr typeexpr) + : name(name), nameId(nameId), typeexpr(typeexpr) {} - std::any accept(Visitor &visitor) override { - return visitor.visitTypeDeclExpr(shared_from_this()); - } + std::shared_ptr eval(Interpreter &interp) override; + std::pair> evalAsFieldDecl(Interpreter &interp); + std::string prettyPrint() override; + std::vector collectVars() override { return {}; } const Token name; + const Symbol nameId; const std::shared_ptr typeexpr; }; -class ListTypeExpr : public Expr, - public std::enable_shared_from_this { +class ListTypeExpr : public Expr { public: ListTypeExpr(std::shared_ptr typeexpr) : typeexpr(typeexpr) {} - std::any accept(Visitor &visitor) override { - return visitor.visitListTypeExprExpr(shared_from_this()); - } + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override { return {}; } + std::shared_ptr resolveTypeExpr(Interpreter &interp) override; const std::shared_ptr typeexpr; }; -class ListVarExpr : public Expr, - public std::enable_shared_from_this { +class ListVarExpr : public Expr { public: ListVarExpr(std::vector> varExprs) : varExprs(varExprs) {} - std::any accept(Visitor &visitor) override { - return visitor.visitListVarExprExpr(shared_from_this()); - } + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override; const std::vector> varExprs; }; -class IfExpr : public Expr, public std::enable_shared_from_this { +class IfExpr : public Expr { public: IfExpr(std::shared_ptr condition, std::shared_ptr thenBranch, std::shared_ptr elseBranch) : condition(condition), thenBranch(thenBranch), elseBranch(elseBranch) {} - std::any accept(Visitor &visitor) override { - return visitor.visitIfExprExpr(shared_from_this()); - } + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override; const std::shared_ptr condition; const std::shared_ptr thenBranch; const std::shared_ptr elseBranch; }; -class Let : public Expr, public std::enable_shared_from_this { +class Let : public Expr { public: Let(std::vector> exprs) : exprs(exprs) {} - std::any accept(Visitor &visitor) override { - return visitor.visitLetExpr(shared_from_this()); - } + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override; const std::vector> exprs; }; -class InitNext : public Expr, public std::enable_shared_from_this { +class InitNext : public Expr { public: InitNext(std::shared_ptr initializer, std::shared_ptr nextExpr) : initializer(initializer), nextExpr(nextExpr) {} - std::any accept(Visitor &visitor) override { - return visitor.visitInitNextExpr(shared_from_this()); - } + std::shared_ptr eval(Interpreter &interp) override; + std::string prettyPrint() override; + std::vector collectVars() override; const std::shared_ptr initializer; const std::shared_ptr nextExpr; diff --git a/include/Stmt.hpp b/include/Stmt.hpp index 6ccf368..f38a671 100644 --- a/include/Stmt.hpp +++ b/include/Stmt.hpp @@ -3,81 +3,64 @@ #include "Expr.hpp" #include "Token.hpp" -#include #include #include #include namespace Autumn { -class Block; -class Object; -class Expression; -class OnStmt; +class Interpreter; class Stmt { public: - // Visitor interface - class Visitor { - public: - virtual std::any visitBlockStmt(std::shared_ptr stmt) = 0; - virtual std::any visitObjectStmt(std::shared_ptr stmt) = 0; - virtual std::any visitExpressionStmt(std::shared_ptr stmt) = 0; - virtual std::any visitOnStmtStmt(std::shared_ptr stmt) = 0; - virtual ~Visitor() = default; - }; - - virtual std::any accept(Visitor &visitor) = 0; + virtual void exec(Interpreter &interp) = 0; + virtual std::string prettyPrint() = 0; virtual ~Stmt() = default; }; -class Block : public Stmt, public std::enable_shared_from_this { +class Block : public Stmt { public: Block(std::vector> statements) : statements(statements) {} - std::any accept(Visitor &visitor) override { - return visitor.visitBlockStmt(shared_from_this()); - } + void exec(Interpreter &interp) override; + std::string prettyPrint() override; const std::vector> statements; }; -class Object : public Stmt, public std::enable_shared_from_this { +class Object : public Stmt { public: - Object(Token name, std::vector> fields, + Object(Token name, Symbol nameId, std::vector> fields, std::shared_ptr Cell) - : name(name), fields(fields), Cell(Cell) {} + : name(name), nameId(nameId), fields(fields), Cell(Cell) {} - std::any accept(Visitor &visitor) override { - return visitor.visitObjectStmt(shared_from_this()); - } + void exec(Interpreter &interp) override; + std::string prettyPrint() override; const Token name; + const Symbol nameId; const std::vector> fields; const std::shared_ptr Cell; }; -class Expression : public Stmt, - public std::enable_shared_from_this { +class Expression : public Stmt { public: Expression(std::shared_ptr expression) : expression(expression) {} - std::any accept(Visitor &visitor) override { - return visitor.visitExpressionStmt(shared_from_this()); - } + void exec(Interpreter &interp) override; + std::string prettyPrint() override; const std::shared_ptr expression; }; -class OnStmt : public Stmt, public std::enable_shared_from_this { +class OnStmt : public Stmt { public: OnStmt(std::shared_ptr condition, std::shared_ptr expr) : condition(condition), expr(expr) {} - std::any accept(Visitor &visitor) override { - return visitor.visitOnStmtStmt(shared_from_this()); - } + void exec(Interpreter &interp) override; + std::string prettyPrint() override; const std::shared_ptr condition; const std::shared_ptr expr; diff --git a/include/interpreter/AutumnCallable.hpp b/include/interpreter/AutumnCallable.hpp index 31c2428..c685bc0 100644 --- a/include/interpreter/AutumnCallable.hpp +++ b/include/interpreter/AutumnCallable.hpp @@ -14,7 +14,6 @@ class AutumnCallable { virtual int arity() = 0; virtual ~AutumnCallable() = default; virtual std::string toString() const { return std::string("callable"); }; - virtual std::shared_ptr clone() = 0; }; } // namespace Autumn diff --git a/include/interpreter/AutumnStdLib.hpp b/include/interpreter/AutumnStdLib.hpp index d08d071..455a013 100644 --- a/include/interpreter/AutumnStdLib.hpp +++ b/include/interpreter/AutumnStdLib.hpp @@ -8,8 +8,7 @@ namespace Autumn { -class RenderAll : public AutumnCallable, - public std::enable_shared_from_this { +class RenderAll : public AutumnCallable { public: RenderAll() {} std::shared_ptr @@ -17,13 +16,9 @@ class RenderAll : public AutumnCallable, const std::vector> &arguments) override; int arity() override { return 0; } std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class Defined : public AutumnCallable, - public std::enable_shared_from_this { +class Defined : public AutumnCallable { public: Defined() {} std::shared_ptr @@ -31,13 +26,9 @@ class Defined : public AutumnCallable, const std::vector> &arguments) override; int arity() override { return 1; } std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class IsFreePos : public AutumnCallable, - public std::enable_shared_from_this { +class IsFreePos : public AutumnCallable { public: IsFreePos() {} std::shared_ptr @@ -45,13 +36,9 @@ class IsFreePos : public AutumnCallable, const std::vector> &arguments) override; int arity() override { return 1; } std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class Print : public AutumnCallable, - public std::enable_shared_from_this { +class Print : public AutumnCallable { public: Print() {} std::shared_ptr @@ -59,13 +46,9 @@ class Print : public AutumnCallable, const std::vector> &arguments) override; int arity() override { return 1; } std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class Range : public AutumnCallable, - public std::enable_shared_from_this { +class Range : public AutumnCallable { public: Range() {} std::shared_ptr @@ -73,13 +56,9 @@ class Range : public AutumnCallable, const std::vector> &arguments) override; int arity() override { return 2; } std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class AllObjs : public AutumnCallable, - public std::enable_shared_from_this { +class AllObjs : public AutumnCallable { public: AllObjs() {} std::shared_ptr @@ -87,13 +66,9 @@ class AllObjs : public AutumnCallable, const std::vector> &arguments) override; int arity() override { return 0; } std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class Rotate : public AutumnCallable, - public std::enable_shared_from_this { +class Rotate : public AutumnCallable { public: Rotate() {} std::shared_ptr @@ -101,13 +76,9 @@ class Rotate : public AutumnCallable, const std::vector> &arguments) override; int arity() override { return 1; } std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class Any : public AutumnCallable, - public std::enable_shared_from_this { +class Any : public AutumnCallable { public: Any() {} std::shared_ptr @@ -115,13 +86,9 @@ class Any : public AutumnCallable, const std::vector> &arguments) override; int arity() override { return 2; } std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class Foldl : public AutumnCallable, - public std::enable_shared_from_this { +class Foldl : public AutumnCallable { public: Foldl() {} std::shared_ptr @@ -129,13 +96,9 @@ class Foldl : public AutumnCallable, const std::vector> &arguments) override; int arity() override { return 3; } std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class IsList : public AutumnCallable, - public std::enable_shared_from_this { +class IsList : public AutumnCallable { public: IsList() {} std::shared_ptr @@ -143,13 +106,9 @@ class IsList : public AutumnCallable, const std::vector> &arguments) override; int arity() override { return 1; } std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class Length : public AutumnCallable, - public std::enable_shared_from_this { +class Length : public AutumnCallable { public: Length() {} std::shared_ptr @@ -157,13 +116,9 @@ class Length : public AutumnCallable, const std::vector> &arguments) override; int arity() override { return 1; } std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class Head : public AutumnCallable, - public std::enable_shared_from_this { +class Head : public AutumnCallable { public: Head() {} std::shared_ptr @@ -171,13 +126,9 @@ class Head : public AutumnCallable, const std::vector> &arguments) override; int arity() override { return 1; } std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class At: public AutumnCallable, - public std::enable_shared_from_this { +class At: public AutumnCallable { public: At() {} std::shared_ptr @@ -185,13 +136,9 @@ class At: public AutumnCallable, const std::vector> &arguments) override; int arity() override { return 2; } std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class Tail : public AutumnCallable, - public std::enable_shared_from_this { +class Tail : public AutumnCallable { public: Tail() {} std::shared_ptr @@ -199,13 +146,9 @@ class Tail : public AutumnCallable, const std::vector> &arguments) override; int arity() override { return 1; } std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class Prev : public AutumnCallable, - public std::enable_shared_from_this { +class Prev : public AutumnCallable { public: Prev() {} std::shared_ptr @@ -213,13 +156,9 @@ class Prev : public AutumnCallable, const std::vector> &arguments) override; int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class UniformChoice : public AutumnCallable, - public std::enable_shared_from_this { +class UniformChoice : public AutumnCallable { public: UniformChoice() {} std::shared_ptr @@ -228,12 +167,8 @@ class UniformChoice : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class IsOutSideBounds : public AutumnCallable, - public std::enable_shared_from_this { +class IsOutSideBounds : public AutumnCallable { public: IsOutSideBounds() {} std::shared_ptr @@ -244,13 +179,9 @@ class IsOutSideBounds : public AutumnCallable, return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class Clicked : public AutumnCallable, - public std::enable_shared_from_this { +class Clicked : public AutumnCallable { public: Clicked() {} std::shared_ptr @@ -259,13 +190,9 @@ class Clicked : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class DownPressed : public AutumnCallable, - public std::enable_shared_from_this { +class DownPressed : public AutumnCallable { public: DownPressed() {} std::shared_ptr @@ -274,13 +201,9 @@ class DownPressed : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class LeftPressed : public AutumnCallable, - public std::enable_shared_from_this { +class LeftPressed : public AutumnCallable { public: LeftPressed() {} std::shared_ptr @@ -289,13 +212,9 @@ class LeftPressed : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class RightPressed : public AutumnCallable, - public std::enable_shared_from_this { +class RightPressed : public AutumnCallable { public: RightPressed() {} std::shared_ptr @@ -304,13 +223,9 @@ class RightPressed : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class UpPressed : public AutumnCallable, - public std::enable_shared_from_this { +class UpPressed : public AutumnCallable { public: UpPressed() {} std::shared_ptr @@ -319,13 +234,9 @@ class UpPressed : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class ObjClicked : public AutumnCallable, - public std::enable_shared_from_this { +class ObjClicked : public AutumnCallable { public: ObjClicked() {} std::shared_ptr @@ -334,12 +245,8 @@ class ObjClicked : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class AddObj : public AutumnCallable, - public std::enable_shared_from_this { +class AddObj : public AutumnCallable { public: AddObj() {} std::shared_ptr @@ -348,13 +255,9 @@ class AddObj : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class RemoveObj : public AutumnCallable, - public std::enable_shared_from_this { +class RemoveObj : public AutumnCallable { public: RemoveObj() {} std::shared_ptr @@ -363,13 +266,9 @@ class RemoveObj : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class UpdateObj : public AutumnCallable, - public std::enable_shared_from_this { +class UpdateObj : public AutumnCallable { public: UpdateObj() {} std::shared_ptr @@ -378,13 +277,9 @@ class UpdateObj : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class AdjPositions : public AutumnCallable, - public std::enable_shared_from_this { +class AdjPositions : public AutumnCallable { public: AdjPositions() {} std::shared_ptr @@ -393,13 +288,9 @@ class AdjPositions : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class IsFree : public AutumnCallable, - public std::enable_shared_from_this { +class IsFree : public AutumnCallable { public: IsFree() {} std::shared_ptr @@ -408,13 +299,9 @@ class IsFree : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class Rect : public AutumnCallable, - public std::enable_shared_from_this { +class Rect : public AutumnCallable { public: Rect() {} std::shared_ptr @@ -423,13 +310,9 @@ class Rect : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class Displacement : public AutumnCallable, - public std::enable_shared_from_this { +class Displacement : public AutumnCallable { public: Displacement() {} std::shared_ptr @@ -438,13 +321,9 @@ class Displacement : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class Adjacent : public AutumnCallable, - public std::enable_shared_from_this { +class Adjacent : public AutumnCallable { public: Adjacent() {} std::shared_ptr @@ -453,13 +332,9 @@ class Adjacent : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class AdjacentObjs : public AutumnCallable, - public std::enable_shared_from_this { +class AdjacentObjs : public AutumnCallable { public: AdjacentObjs() {} std::shared_ptr @@ -468,13 +343,9 @@ class AdjacentObjs : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class AdjacentObjsDiagonal : public AutumnCallable, - public std::enable_shared_from_this { +class AdjacentObjsDiagonal : public AutumnCallable { public: AdjacentObjsDiagonal() {} std::shared_ptr @@ -485,13 +356,9 @@ class AdjacentObjsDiagonal : public AutumnCallable, return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class AdjacentDiag : public AutumnCallable, - public std::enable_shared_from_this { +class AdjacentDiag : public AutumnCallable { public: AdjacentDiag() {} std::shared_ptr @@ -500,13 +367,9 @@ class AdjacentDiag : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class Adj : public AutumnCallable, - public std::enable_shared_from_this { +class Adj : public AutumnCallable { public: Adj() {} std::shared_ptr @@ -515,13 +378,9 @@ class Adj : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class Map : public AutumnCallable, - public std::enable_shared_from_this { +class Map : public AutumnCallable { public: Map() {} std::shared_ptr @@ -530,9 +389,6 @@ class Map : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } private: std::shared_ptr mapSequential(Interpreter &interpreter, std::shared_ptr callable, @@ -542,8 +398,7 @@ class Map : public AutumnCallable, const std::vector> &arguments); }; -class Filter : public AutumnCallable, - public std::enable_shared_from_this { +class Filter : public AutumnCallable { public: Filter() {} std::shared_ptr @@ -552,13 +407,9 @@ class Filter : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class IsWithinBounds : public AutumnCallable, - public std::enable_shared_from_this { +class IsWithinBounds : public AutumnCallable { public: IsWithinBounds() {} std::shared_ptr @@ -569,13 +420,9 @@ class IsWithinBounds : public AutumnCallable, return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class RandomPositions : public AutumnCallable, - public std::enable_shared_from_this { +class RandomPositions : public AutumnCallable { public: RandomPositions() {} std::shared_ptr @@ -586,13 +433,9 @@ class RandomPositions : public AutumnCallable, return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class AllPositions : public AutumnCallable, - public std::enable_shared_from_this { +class AllPositions : public AutumnCallable { public: AllPositions() {} std::shared_ptr @@ -601,13 +444,9 @@ class AllPositions : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class Concat : public AutumnCallable, - public std::enable_shared_from_this { +class Concat : public AutumnCallable { public: Concat() {} std::shared_ptr @@ -616,13 +455,9 @@ class Concat : public AutumnCallable, int arity() override; std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class ArrayEqual : public AutumnCallable, - public std::enable_shared_from_this { +class ArrayEqual : public AutumnCallable { public: ArrayEqual() {} std::shared_ptr @@ -630,13 +465,9 @@ class ArrayEqual : public AutumnCallable, const std::vector> &arguments) override; int arity() override { return 2; } std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; -class Sqrt : public AutumnCallable, - public std::enable_shared_from_this { +class Sqrt : public AutumnCallable { public: Sqrt() {} std::shared_ptr @@ -644,9 +475,6 @@ class Sqrt : public AutumnCallable, const std::vector> &arguments) override; int arity() override { return 1; } std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return shared_from_this(); - } }; } // namespace Autumn diff --git a/include/interpreter/Environment.hpp b/include/interpreter/Environment.hpp index f6dfb81..77575cb 100644 --- a/include/interpreter/Environment.hpp +++ b/include/interpreter/Environment.hpp @@ -3,6 +3,7 @@ #include "AutumnType.hpp" #include "AutumnValue.hpp" +#include "Interner.hpp" #include "Token.hpp" #include #include @@ -25,35 +26,40 @@ struct pair_hash { } }; -class Environment : public std::enable_shared_from_this { +class Environment { private: - std::unordered_map> values; + std::unordered_map> values; - std::unordered_map updateStates; - std::unordered_map> typeValues; - std::unordered_map assignedTypes; + std::unordered_map updateStates; + std::unordered_map> typeValues; + std::unordered_map assignedTypes; - std::vector definitionOrder; + std::vector definitionOrder; std::unordered_set, pair_hash> occupiedPositions; EnvironmentType environmentType; + // Used by the string-based overloads to intern on-the-fly. Inherited from + // the enclosing environment for sub-scopes; must be provided for globals. + Interner *interner_; + protected: EnvironmentPtr enclosing; - + public: // Constructors - // Default constructor: no enclosing environment (e.g., global scope) - Environment(); + // Top-level constructor: caller must supply the Interner that will outlive + // this environment and all its nested scopes. + explicit Environment(Interner &interner); - // Constructor with an enclosing environment + // Constructor with an enclosing environment: interner is inherited. Environment(EnvironmentPtr enclosingEnv, EnvironmentType environmentType=EnvironmentType::LOCAL); - const std::vector &getDefinitionOrder() { - return definitionOrder; - } + const std::vector &getDefinitionOrder() { return definitionOrder; } + + Interner &getInterner() { return *interner_; } void clearOccupied() { occupiedPositions.clear(); } @@ -63,75 +69,98 @@ class Environment : public std::enable_shared_from_this { return occupiedPositions.find({x, y}) == occupiedPositions.end(); } - // Define a new variable in the current environment - void define(std::string name, std::shared_ptr value); + // --- Symbol-based hot-path APIs --- + void define(Symbol name, std::shared_ptr value); + void defineType(Symbol name, std::shared_ptr type); + std::shared_ptr get(Symbol name); + std::shared_ptr getTypeValue(Symbol name); + void assign(Symbol name, const std::shared_ptr &value); + void assignType(Symbol name, const std::shared_ptr &type); + + bool isDefined(Symbol name) { return values.find(name) != values.end(); } + // --- String-based overloads (intern on call; callers that have a pre- + // computed Symbol should prefer the Symbol-based versions above). --- + void define(const std::string &name, std::shared_ptr value) { + define(interner_->intern(name), std::move(value)); + } + void defineType(const std::string &name, std::shared_ptr type) { + defineType(interner_->intern(name), std::move(type)); + } + std::shared_ptr get(const std::string &name) { + return get(interner_->intern(name)); + } + std::shared_ptr get(const Token &name) { + return get(interner_->intern(name.lexeme)); + } + std::shared_ptr getTypeValue(const Token &name) { + return getTypeValue(interner_->intern(name.lexeme)); + } + void assign(const Token &name, const std::shared_ptr &value) { + assign(interner_->intern(name.lexeme), value); + } + void assign(const std::string &name, + const std::shared_ptr &value) { + assign(interner_->intern(name), value); + } + void assignType(const std::string &name, + const std::shared_ptr &type) { + assignType(interner_->intern(name), type); + } bool isDefined(const std::string &name) { - return values.find(name) != values.end(); + return values.find(interner_->intern(name)) != values.end(); } - void defineType(std::string name, std::shared_ptr type); - // Get the value of a variable at a certain distance in the environment - // chain + // Get the value of a variable at a certain distance in the environment chain std::shared_ptr getAt(int distance, const std::string &name); - // Assign a new value to a variable at a certain distance in the environment - // chain + // Assign a new value to a variable at a certain distance in the environment chain void assignAt(int distance, const Token &name, const std::shared_ptr &value); - // Retrieve the value of a variable, searching enclosing environments if - // necessary - std::shared_ptr get(const Token &name); - std::shared_ptr get(const std::string &name); - - std::shared_ptr getTypeValue(const Token &name); - - // Assign a new value to a variable, searching enclosing environments if - // necessary - void assign(const Token &name, const std::shared_ptr &value); - void assign(const std::string &name, - const std::shared_ptr &value); - - void assignType(std::string name, const std::shared_ptr &type); - std::shared_ptr getEnclosing() { return enclosing; } - std::shared_ptr getAssignedType(std::string name) { - if (assignedTypes.find(name) != assignedTypes.end()) { - return std::any_cast>(assignedTypes[name]); + std::shared_ptr getAssignedType(Symbol name) { + if (auto it = assignedTypes.find(name); it != assignedTypes.end()) { + return std::any_cast>(it->second); } if (enclosing != nullptr) { return enclosing->getAssignedType(name); } return nullptr; - }; + } + std::shared_ptr getAssignedType(const std::string &name) { + return getAssignedType(interner_->intern(name)); + } void resetUpdateStates(); std::string printAllAssignedTypes() { std::string result = ""; for (const auto &[key, value] : assignedTypes) { - result += key + " : " + + result += *key + " : " + std::any_cast>(value)->toString() + "\n"; } return result; } - bool isUpdated(const std::string &name) { - if (updateStates.find(name) != updateStates.end()) { - return updateStates[name]; + bool isUpdated(Symbol name) { + if (auto it = updateStates.find(name); it != updateStates.end()) { + return it->second; } if (enclosing != nullptr) { return enclosing->isUpdated(name); } return false; } + bool isUpdated(const std::string &name) { + return isUpdated(interner_->intern(name)); + } std::string printAllDefinedVariables() { std::string result = ""; for (const auto &[key, value] : values) { - result += key + " : " + value->toString() + "\n"; + result += *key + " : " + value->toString() + "\n"; } return result; } @@ -139,7 +168,7 @@ class Environment : public std::enable_shared_from_this { std::string printAllDefinedVariablesCrossStack() { std::string result = ""; for (const auto &[key, value] : values) { - result += key + " : " + value->toString() + "\n"; + result += *key + " : " + value->toString() + "\n"; } if (enclosing != nullptr) { result += enclosing->printAllDefinedVariablesCrossStack(); @@ -147,7 +176,7 @@ class Environment : public std::enable_shared_from_this { return result; } - const std::unordered_map> & + const std::unordered_map> & getDefinedVariables() { return values; } @@ -155,40 +184,27 @@ class Environment : public std::enable_shared_from_this { std::string printAllTypeValues() { std::string result = ""; for (const auto &[key, value] : typeValues) { - result += key + " : " + - std::any_cast>(value)->toString() + - "\n"; + result += *key + " : " + value->toString() + "\n"; } return result; } EnvironmentPtr copy(EnvironmentPtr copiedEnclosing) { EnvironmentPtr newEnv = std::make_shared(copiedEnclosing, environmentType); - // Deep copy value - std::unordered_map> values; - for (const auto &[key, value] : this->values) { - values[key] = value->clone(); - } - newEnv->values = values; - // Deep copy update states - std::unordered_map updateStates; - for (const auto &[key, value] : this->updateStates) { - updateStates[key] = value; - } - // Deep copy type values - std::unordered_map> typeValues; - for (const auto &[key, value] : this->typeValues) { - typeValues[key] = value; - } - newEnv->updateStates = updateStates; - newEnv->assignedTypes = assignedTypes; - newEnv->typeValues = typeValues; + // Constructor only inherits interner from the enclosing env; when + // copiedEnclosing is nullptr (top-level of the copied chain) we must + // propagate our own interner so the new env's string-based APIs work. + newEnv->interner_ = this->interner_; + newEnv->values = this->values; + newEnv->updateStates = this->updateStates; + newEnv->assignedTypes = this->assignedTypes; + newEnv->typeValues = this->typeValues; return newEnv; } void copyAll(EnvironmentPtr from) { for (const auto &[key, value] : from->values) { - values[key] = value->clone(); + values[key] = value; } for (const auto &[key, value] : from->typeValues) { typeValues[key] = value; @@ -204,29 +220,24 @@ class Environment : public std::enable_shared_from_this { } } - void selectiveCopy(EnvironmentPtr from, std::vector keys) { + void selectiveCopy(EnvironmentPtr from, const std::vector &keys) { + auto inKeys = [&](Symbol k) { + return std::find(keys.begin(), keys.end(), k) != keys.end(); + }; for (const auto &[key, value] : from->values) { - if (std::find(keys.begin(), keys.end(), key) != keys.end()) { - values[key] = value->clone(); - } + if (inKeys(key)) values[key] = value; } for (const auto &[key, value] : from->typeValues) { typeValues[key] = value; } for (const auto &[key, value] : from->updateStates) { - if (std::find(keys.begin(), keys.end(), key) != keys.end()) { - updateStates[key] = value; - } + if (inKeys(key)) updateStates[key] = value; } for (const auto &[key, value] : from->assignedTypes) { - if (std::find(keys.begin(), keys.end(), key) != keys.end()) { - assignedTypes[key] = value; - } + if (inKeys(key)) assignedTypes[key] = value; } for (auto key : from->definitionOrder) { - if (std::find(keys.begin(), keys.end(), key) != keys.end()) { - definitionOrder.push_back(key); - } + if (inKeys(key)) definitionOrder.push_back(key); } } @@ -235,6 +246,7 @@ class Environment : public std::enable_shared_from_this { std::shared_ptr findId(int instId) { for (const auto &[key, value] : values) { + (void)key; if (value->getInstId() == instId) { return value; } @@ -262,21 +274,14 @@ class Environment : public std::enable_shared_from_this { std::string toJson(std::string childScope = ""){ - // Need to encode: values, updateStates, typeValues, assignedTypes - // std::unordered_map> values; - - // EnvironmentPtr enclosing; - // std::unordered_map updateStates; - // std::unordered_map> typeValues; - // std::unordered_map assignedTypes; std::string result = ""; std::unordered_map varScope; for (const auto &[key, value] : values) { - varScope[key] = value->toString(); + varScope[*key] = value->toString(); } std::unordered_map typeScope; for (const auto &[key, value] : typeValues) { - typeScope[key] = std::any_cast>(value)->toString(); + typeScope[*key] = value->toString(); } // Indent child scope std::string indent = " "; @@ -286,14 +291,13 @@ class Environment : public std::enable_shared_from_this { while (std::getline(ss, line)) { childScopeLines.push_back(line); } - for (int i = 0; i < childScopeLines.size(); i++) { + for (size_t i = 0; i < childScopeLines.size(); i++) { childScopeLines[i] = indent + childScopeLines[i]; } result += "{\n"; result += indent + "\"varValues\": {\n"; for (const auto &[key, value] : varScope) { std::string valueStr = value; - // Sanitize the value string to be a valid JSON string valueStr = std::regex_replace(valueStr, std::regex("\\n"), "\\n"); valueStr = std::regex_replace(valueStr, std::regex("\\r"), "\\r"); valueStr = std::regex_replace(valueStr, std::regex("\\t"), "\\t"); @@ -306,10 +310,10 @@ class Environment : public std::enable_shared_from_this { result += indent + " \"" + key + "\": \"" + value + "\",\n"; } result += indent + " }" + (childScope != "" ? ",\n" : "\n"); - if (childScope != "") { // Only add child scope if it is not empty + if (childScope != "") { result += indent + "\"childScope\": \n"; - for (const auto &line : childScopeLines) { - result += line + "\n"; + for (const auto &l : childScopeLines) { + result += l + "\n"; } } result += "}\n"; @@ -326,7 +330,7 @@ class Environment : public std::enable_shared_from_this { private: // Helper function to traverse to the ancestor environment at a given distance - EnvironmentPtr ancestor(int distance); + Environment *ancestor(int distance); }; } // namespace Autumn diff --git a/include/interpreter/Interner.hpp b/include/interpreter/Interner.hpp new file mode 100644 index 0000000..ca69f7b --- /dev/null +++ b/include/interpreter/Interner.hpp @@ -0,0 +1,32 @@ +#ifndef _AUTUMN_INTERNER_HPP_ +#define _AUTUMN_INTERNER_HPP_ + +#include +#include + +namespace Autumn { + +// An interned identifier. Pointer identity == string equality. +// Pointer is stable for the lifetime of the owning Interner because +// std::unordered_set guarantees node address stability across insertions. +using Symbol = const std::string *; + +class Interner { +public: + // Returns a stable pointer. Calling intern() with equal strings returns + // the same pointer. Called at parse time, not in hot paths — the + // std::string construction on probe is fine. + Symbol intern(const std::string &s) { + auto [it, _] = pool_.emplace(s); + return &*it; + } + + size_t size() const { return pool_.size(); } + +private: + std::unordered_set pool_; +}; + +} // namespace Autumn + +#endif diff --git a/include/interpreter/Interpreter.hpp b/include/interpreter/Interpreter.hpp index 5019872..e3dcf20 100644 --- a/include/interpreter/Interpreter.hpp +++ b/include/interpreter/Interpreter.hpp @@ -3,114 +3,104 @@ #include "Environment.hpp" #include "Error.hpp" #include "Expr.hpp" +#include "Interner.hpp" #include "State.hpp" #include "Stmt.hpp" #include "Token.hpp" -#include #include #include #include #include +#include #include #include #include "RandomGenerator.hpp" namespace Autumn { -struct SharedExprPtrHash { - std::size_t operator()(const std::shared_ptr &expr) const noexcept { - return std::hash()(expr.get()); - } -}; -// Define the equality function for std::shared_ptr -struct SharedExprPtrEqual { - bool operator()(const std::shared_ptr &lhs, - const std::shared_ptr &rhs) const noexcept { - return lhs.get() == rhs.get(); - } -}; +class Interpreter { + // Must be constructed first and destroyed last — Token/AST nodes and + // Environment maps hold interned Symbol* pointers into this pool. + Interner interner_; -class Interpreter : public Expr::Visitor, public Stmt::Visitor { - std::unordered_map, int, SharedExprPtrHash, - SharedExprPtrEqual> - locals; - - const std::shared_ptr state = std::make_shared(); + State state; std::stack> tmpEnvStack; - std::shared_ptr globals = std::make_shared(); + std::shared_ptr globals = std::make_shared(interner_); std::shared_ptr environment = globals; std::shared_ptr prev_environment = nullptr; - const std::vector> stmts; - std::shared_ptr randomGen; bool verbose = false; - - bool isTruthy(std::shared_ptr object) { - return object->isTruthy(); - } - - - - std::any evaluate(std::shared_ptr expr) { return expr->accept(*this); } - - - bool isEqual(std::shared_ptr a, std::shared_ptr b) { - if (a == nullptr && b == nullptr) { - return true; - } - if (a == nullptr) { - return false; - } - return a->isEqual(b); - } - void init(std::string = ""); enum InterpretingState { NONE, OBJECT }; std::stack stateStack; - std::vector initOrder; - std::unordered_map> initMap; - std::unordered_map> nextMap; + // Variables declared with (= name (initnext init-expr next-expr)). + // Stored in declaration order so iteration (in start() and step()) is + // deterministic and matches program text. Replaces the previous + // (initOrder + initMap + nextMap) trio — deterministic, cache-friendly, + // and no hash lookup on the hot path. + struct InitNextVar { + Symbol name; + std::shared_ptr initExpr; + std::shared_ptr nextExpr; + }; + std::vector initNextVars; std::vector> onStmts; std::shared_ptr triggeringConditionExpr = nullptr; std::unordered_set onClauseCovered; public: - std::string evaluateToString(std::string expr) ; + // Allow Expr/Stmt subclasses to access interpreter internals for eval/exec + friend class Assign; + friend class Binary; + friend class Call; + friend class Get; + friend class Grouping; + friend class IntLiteral; + friend class BoolLiteral; + friend class StringLiteral; + friend class Logical; + friend class Set; + friend class Unary; + friend class Lambda; + friend class Variable; + friend class TypeVariable; + friend class TypeDecl; + friend class ListTypeExpr; + friend class ListVarExpr; + friend class IfExpr; + friend class Let; + friend class InitNext; + friend class Block; + friend class Object; + friend class Expression; + friend class OnStmt; + + std::string evaluateToString(std::string expr); int getOnClauseCount() { return onStmts.size(); } int getCoveredOnClauseCount() { return onClauseCovered.size(); } + const std::unordered_set& getCoveredOnClauseIndices() { return onClauseCovered; } void start(const std::vector> &stmts, std::string = "", std::string = "", uint64_t randomSeed = 0); void step(); void reloadCode(const std::vector> &stmts, std::string stdlib, std::string triggeringCondition); void tmpExecuteStmt(const std::shared_ptr &stmt); - std::shared_ptr getState() { return state; } - - std::any lookUpVariable(Token name, std::shared_ptr expr) { - auto distance = locals.find(expr); - if (distance != locals.end()) { - return environment->getAt(distance->second, name.lexeme); - } else { - return globals->get(name); - } - } - - void resolve(std::shared_ptr expr, int depth) { locals[expr] = depth; } + State &getState() { return state; } const std::shared_ptr &getGlobals() { return globals; } const std::shared_ptr &getPrevEnvironment() { return prev_environment; } std::shared_ptr getEnvironment() { return environment; } + Interner &getInterner() { return interner_; } Interpreter(); bool getTriggerState() { - // Check if it has been defined in global if (globals->get("SpecialConditionTriggered") != nullptr) { return std::dynamic_pointer_cast( globals->get("SpecialConditionTriggered")) @@ -120,32 +110,6 @@ class Interpreter : public Expr::Visitor, public Stmt::Visitor { } } - // Visiting functions for expressions - std::any visitLiteralExpr(std::shared_ptr expr) override; - std::any visitGroupingExpr(std::shared_ptr expr) override; - std::any visitUnaryExpr(std::shared_ptr expr) override; - std::any visitBinaryExpr(std::shared_ptr expr) override; - std::any visitVariableExpr(std::shared_ptr expr) override; - std::any visitAssignExpr(std::shared_ptr expr) override; - std::any visitLogicalExpr(std::shared_ptr expr) override; - std::any visitCallExpr(std::shared_ptr expr) override; - std::any visitGetExpr(std::shared_ptr expr) override; - std::any visitSetExpr(std::shared_ptr expr) override; - std::any visitLambdaExpr(std::shared_ptr expr) override; - std::any visitTypeVariableExpr(std::shared_ptr expr) override; - std::any visitTypeDeclExpr(std::shared_ptr expr) override; - std::any visitListTypeExprExpr(std::shared_ptr expr) override; - std::any visitListVarExprExpr(std::shared_ptr expr) override; - std::any visitIfExprExpr(std::shared_ptr expr) override; - std::any visitLetExpr(std::shared_ptr expr) override; - std::any visitInitNextExpr(std::shared_ptr expr) override; - - // Visiting functions for statements - std::any visitBlockStmt(std::shared_ptr stmt) override; - std::any visitObjectStmt(std::shared_ptr stmt) override; - std::any visitExpressionStmt(std::shared_ptr stmt) override; - std::any visitOnStmtStmt(std::shared_ptr stmt) override; - void setEnvironment(std::shared_ptr env) { environment = env; } std::string renderAll(); @@ -170,7 +134,7 @@ class Interpreter : public Expr::Visitor, public Stmt::Visitor { randomGen->setSeed(seed); } } - + std::shared_ptr getRandomGenerator() const { return randomGen; } diff --git a/include/interpreter/VarCollector.hpp b/include/interpreter/VarCollector.hpp index c096999..9eda96b 100644 --- a/include/interpreter/VarCollector.hpp +++ b/include/interpreter/VarCollector.hpp @@ -1,133 +1,7 @@ #ifndef __AUTUMN_VARIABLE_COLLECTOR_HPP__ #define __AUTUMN_VARIABLE_COLLECTOR_HPP__ -#include -#include -#include -#include "Expr.hpp" -#include "Stmt.hpp" -#include "Error.hpp" +// VarCollector functionality is now provided by Expr::collectVars() virtual method. +// This header is kept for backward compatibility with existing includes. -namespace Autumn { - -class VariableCollector: public Expr::Visitor, public Stmt::Visitor { - - public: - // Visiting functions for expressions - std::any visitLiteralExpr(std::shared_ptr expr) override{ - return std::make_shared>(); - } - std::any visitGroupingExpr(std::shared_ptr expr) override{ - throw Error("This language does not supposed to have grouping :)"); - } - std::any visitUnaryExpr(std::shared_ptr expr) override{ - return expr->right->accept(*this); - } - std::any visitBinaryExpr(std::shared_ptr expr) override{ - std::shared_ptr> left = std::any_cast>>(expr->left->accept(*this)); - std::shared_ptr> right = std::any_cast>>(expr->right->accept(*this)); - left->insert(left->end(), right->begin(), right->end()); - return left; - } - std::any visitVariableExpr(std::shared_ptr expr) override{ - return std::shared_ptr>(std::make_shared>(1, expr->name.lexeme)); - } - std::any visitAssignExpr(std::shared_ptr expr) override{ - return expr->value->accept(*this); - } - std::any visitLogicalExpr(std::shared_ptr expr) override{ - std::shared_ptr> left = std::any_cast>>(expr->left->accept(*this)); - std::shared_ptr> right = std::any_cast>>(expr->right->accept(*this)); - left->insert(left->end(), right->begin(), right->end()); - return left; - } - std::any visitCallExpr(std::shared_ptr expr) override{ - std::shared_ptr> callee = std::any_cast>>(expr->callee->accept(*this)); - for (auto argument : expr->arguments) { - auto subVarExprs = std::any_cast>>(argument->accept(*this)); - callee->insert(callee->end(), subVarExprs->begin(), subVarExprs->end()); - } - return callee; - } - std::any visitGetExpr(std::shared_ptr expr) override { - auto result = std::shared_ptr>(std::make_shared>()); - result->push_back(expr->name.lexeme); - auto objectVarExprs = std::any_cast>>(expr->object->accept(*this)); - result->insert(result->end(), objectVarExprs->begin(), objectVarExprs->end()); - return result; - } - std::any visitSetExpr(std::shared_ptr expr) override{ - return expr->value->accept(*this); - } - std::any visitLambdaExpr(std::shared_ptr expr) override{ - return expr->right->accept(*this); - } - std::any visitTypeVariableExpr(std::shared_ptr expr) override{ - return std::make_shared>(); - } - std::any visitTypeDeclExpr(std::shared_ptr expr) override{ - return std::make_shared>(); - } - std::any visitListTypeExprExpr(std::shared_ptr expr) override{ - return std::make_shared>(); - } - std::any visitListVarExprExpr(std::shared_ptr expr) override{ - std::shared_ptr> varExprs = std::make_shared>(); - varExprs->reserve(expr->varExprs.size()); - for (auto varExpr : expr->varExprs) { - auto subVarExprs = std::any_cast>>(varExpr->accept(*this)); - varExprs->insert(varExprs->end(), subVarExprs->begin(), subVarExprs->end()); - } - return varExprs; - } - std::any visitIfExprExpr(std::shared_ptr expr) override{ - std::shared_ptr> condition = std::any_cast>>(expr->condition->accept(*this)); - std::shared_ptr> thenBranch = std::any_cast>>(expr->thenBranch->accept(*this)); - std::shared_ptr> elseBranch = std::any_cast>>(expr->elseBranch->accept(*this)); - condition->insert(condition->end(), thenBranch->begin(), thenBranch->end()); - condition->insert(condition->end(), elseBranch->begin(), elseBranch->end()); - return condition; - } - std::any visitLetExpr(std::shared_ptr expr) override{ - std::shared_ptr> result = std::make_shared>(); - result->reserve(expr->exprs.size()); - for (auto expr : expr->exprs) { - auto subVarExprs = std::any_cast>>(expr->accept(*this)); - result->insert(result->end(), subVarExprs->begin(), subVarExprs->end()); - } - return result; - } - - // Visiting functions for statements - std::any visitBlockStmt(std::shared_ptr stmt) override { - throw Error("Block statement is not allowed in this language"); - } - std::any visitObjectStmt(std::shared_ptr stmt) override{ - std::shared_ptr> result = std::make_shared>(); - result->reserve(stmt->fields.size()); - for (auto field : stmt->fields) { - auto subVarExprs = std::any_cast>>(field->accept(*this)); - result->insert(result->end(), subVarExprs->begin(), subVarExprs->end()); - } - return result; - } - std::any visitExpressionStmt(std::shared_ptr stmt) override{ - return stmt->expression->accept(*this); - } - std::any visitOnStmtStmt(std::shared_ptr stmt) override{ - std::shared_ptr> condition = std::any_cast>>(stmt->condition->accept(*this)); - std::shared_ptr> expr = std::any_cast>>(stmt->expr->accept(*this)); - condition->insert(condition->end(), expr->begin(), expr->end()); - return condition; - } - - std::any visitInitNextExpr(std::shared_ptr expr) override{ - std::shared_ptr> initializer = std::any_cast>>(expr->initializer->accept(*this)); - std::shared_ptr> nextExpr = std::any_cast>>(expr->nextExpr->accept(*this)); - initializer->insert(initializer->end(), nextExpr->begin(), nextExpr->end()); - return initializer; - } -}; - -} -#endif // !__AUTUMN_VARIABLE_COLLECTOR_HPP__ \ No newline at end of file +#endif diff --git a/include/interpreter/typesystem/AutumnType.hpp b/include/interpreter/typesystem/AutumnType.hpp index 019b8dd..578db8a 100644 --- a/include/interpreter/typesystem/AutumnType.hpp +++ b/include/interpreter/typesystem/AutumnType.hpp @@ -3,69 +3,79 @@ #include #include -#include namespace Autumn { class AutumnType { public: - virtual std::string toString() const = 0; + // Primary serialization: subclasses override this. + // O(n) append with an accumulator — basically a Haskell difference list. + virtual void toString(std::string &acc) const = 0; + + // Convenience wrapper. Non-virtual: one definition for every subclass. + std::string toString() const { + std::string s; + toString(s); + return s; + } + virtual ~AutumnType() = default; virtual bool operator==(const AutumnType &other) const { return toString() == other.toString(); } + +protected: + // Passkey token used by nullary-singleton subclasses. Nested inside + // AutumnType so only code inside this file can construct one, which + // keeps the subclass constructors "private" for callers while still + // allowing std::make_shared to invoke them. + struct SingletonKey { + explicit SingletonKey() = default; + }; }; class AutumnNumberType : public AutumnType { - static std::shared_ptr instance; - public: + explicit AutumnNumberType(SingletonKey) {} + using AutumnType::toString; static std::shared_ptr getInstance() { - if (instance == nullptr) { - instance = std::make_shared(); - } + static auto instance = std::make_shared(SingletonKey{}); return instance; } - std::string toString() const override { return "Number"; } + void toString(std::string &acc) const override { acc += "Number"; } }; class AutumnStringType : public AutumnType { - static std::shared_ptr instance; - public: + explicit AutumnStringType(SingletonKey) {} + using AutumnType::toString; static std::shared_ptr getInstance() { - if (instance == nullptr) { - instance = std::make_shared(); - } + static auto instance = std::make_shared(SingletonKey{}); return instance; } - std::string toString() const override { return "String"; } + void toString(std::string &acc) const override { acc += "String"; } }; class AutumnBoolType : public AutumnType { - static std::shared_ptr instance; - public: + explicit AutumnBoolType(SingletonKey) {} + using AutumnType::toString; static std::shared_ptr getInstance() { - if (instance == nullptr) { - instance = std::make_shared(); - } + static auto instance = std::make_shared(SingletonKey{}); return instance; } - std::string toString() const override { return "Bool"; } + void toString(std::string &acc) const override { acc += "Bool"; } }; class AutumnUnknownType : public AutumnType { - static std::shared_ptr instance; - public: + explicit AutumnUnknownType(SingletonKey) {} + using AutumnType::toString; bool operator==(const AutumnType &other) const override { return false; } static std::shared_ptr getInstance() { - if (instance == nullptr) { - instance = std::make_shared(); - } + static auto instance = std::make_shared(SingletonKey{}); return instance; } - std::string toString() const override { return "Unknown"; } + void toString(std::string &acc) const override { acc += "Unknown"; } }; class AutumnListType : public AutumnType { @@ -73,6 +83,7 @@ class AutumnListType : public AutumnType { std::shared_ptr elementType; public: + using AutumnType::toString; AutumnListType(std::shared_ptr elementType) : elementType(elementType) {} @@ -96,8 +107,10 @@ class AutumnListType : public AutumnType { return elementType->toString() == otherListType->elementType->toString(); } - std::string toString() const override { - return "List<" + elementType->toString() + ">"; + void toString(std::string &acc) const override { + acc += "List<"; + elementType->toString(acc); + acc += ">"; } std::shared_ptr getElementType() const { return elementType; } @@ -108,6 +121,7 @@ class AutumnMetaType : public AutumnType { std::shared_ptr type; public: + using AutumnType::toString; AutumnMetaType(std::shared_ptr type) : type(type) {} static std::shared_ptr getInstance() { @@ -130,8 +144,10 @@ class AutumnMetaType : public AutumnType { return type->toString() == otherMetaType->type->toString(); } - std::string toString() const override { - return "Meta<" + type->toString() + ">"; + void toString(std::string &acc) const override { + acc += "Meta<"; + type->toString(acc); + acc += ">"; } std::shared_ptr getType() const { return type; } diff --git a/include/interpreter/valuesystem/AutumnCallableValue.hpp b/include/interpreter/valuesystem/AutumnCallableValue.hpp index 82740eb..7ff60fa 100644 --- a/include/interpreter/valuesystem/AutumnCallableValue.hpp +++ b/include/interpreter/valuesystem/AutumnCallableValue.hpp @@ -11,53 +11,56 @@ namespace Autumn { class AutumnFuncType : public AutumnType { - public: - static std::shared_ptr instance; - std::string toString() const { return std::string("AutumnFunc"); } + explicit AutumnFuncType(SingletonKey) {} + using AutumnType::toString; + void toString(std::string &acc) const override { acc += "AutumnFunc"; } static std::shared_ptr getInstance() { - if (instance == nullptr) { - instance = std::make_shared(); - } + static auto instance = std::make_shared(SingletonKey{}); return instance; } }; -class AutumnCallableValue : public AutumnValue, - public std::enable_shared_from_this { +class AutumnCallableValue : public AutumnValue { std::string callableString; public: std::shared_ptr callable; AutumnCallableValue(std::shared_ptr callable) - : AutumnValue(callable->clone(), AutumnFuncType::getInstance()), - callable(callable->clone()) { + : AutumnValue(AutumnFuncType::getInstance()), + callable(callable) { if (callable == nullptr) { throw Error("Error Initializing AutumnCallableValue: callable is null"); } - AstPrinter printer; - callableString = std::any_cast(callable->toString()); + callableString = callable->toString(); } AutumnCallableValue(int instId, std::shared_ptr callable) - : AutumnValue(instId, callable->clone(), AutumnFuncType::getInstance()), - callable(callable->clone()) { + : AutumnValue(instId, AutumnFuncType::getInstance()), + callable(callable) { if (callable == nullptr) { throw Error( "Error Initializing Cloned AutumnCallableValue: callable is null"); } - AstPrinter printer; - callableString = std::any_cast(callable->toString()); + callableString = callable->toString(); } - bool isEqual(std::shared_ptr other) override { - std::shared_ptr otherLambda = - std::dynamic_pointer_cast(other); - if (otherLambda == nullptr) { - return false; + std::shared_ptr binop_on(AutumnValue &rhs, const Token &op) override { + switch (op.type) { + case TokenType::EQUAL_EQUAL: + return std::make_shared(equal_to(rhs)); + case TokenType::BANG_EQUAL: + return std::make_shared(!equal_to(rhs)); + default: + throw Error("Operator not supported on callables"); } - return callable == otherLambda->callable; + } + + bool equal_to(AutumnValue &rhs) override { return rhs.equal_by_callable(*this); } + + bool equal_by_callable(AutumnCallableValue &lhs) override { + return lhs.callable == callable; } virtual std::shared_ptr @@ -72,19 +75,8 @@ class AutumnCallableValue : public AutumnValue, return std::string("toString() + ">"; } - std::shared_ptr clone() override { - if (callable == nullptr) { - throw Error("Error in CallableValue::clone - callable is null"); - } - // Return self instead to save memory and avoid extra clone - return shared_from_this(); - } - std::shared_ptr copy() override { - if (callable == nullptr) { - throw Error("Error in CallableValue::copy - callable is null"); - } - return shared_from_this(); + return std::make_shared(callable); } }; } // namespace Autumn diff --git a/include/interpreter/valuesystem/AutumnClass.hpp b/include/interpreter/valuesystem/AutumnClass.hpp index b80f43c..7e8c8c8 100644 --- a/include/interpreter/valuesystem/AutumnClass.hpp +++ b/include/interpreter/valuesystem/AutumnClass.hpp @@ -3,6 +3,7 @@ #include "AutumnCallable.hpp" #include "AutumnCallableValue.hpp" #include "AutumnType.hpp" +#include namespace Autumn { class AutumnInstance; @@ -33,15 +34,6 @@ class AutumnMethod : public AutumnCallableValue { std::string toString() const override; - bool isEqual(std::shared_ptr other) override { - std::shared_ptr otherMethod = - std::dynamic_pointer_cast(other); - if (otherMethod == nullptr) { - return false; - } - return callable == otherMethod->callable; - } - void setInstance(std::shared_ptr instance) { if (instance == nullptr) { std::cerr << "Error Binding Method Instance: instance is null" @@ -51,12 +43,8 @@ class AutumnMethod : public AutumnCallableValue { this->instance = instance; } - std::shared_ptr clone() override { - return shared_from_this(); - } - std::shared_ptr copy() override { - return shared_from_this(); + return std::make_shared(callable); } }; @@ -87,15 +75,18 @@ class AutumnClass : public AutumnType { const std::vector &getFieldNames() { return fieldnames; } std::shared_ptr getInitializer() { return initializer; } - std::string toString() const override { - std::string results = "class " + name + " {"; + using AutumnType::toString; + void toString(std::string &acc) const override { + acc += "class "; + acc += name; + acc += " {"; for (size_t i = 0; i < fieldnames.size(); i++) { - results += fieldnames[i] + ": " + fieldtypes[i]->toString() + ", "; + acc += fieldnames[i]; + acc += ": "; + fieldtypes[i]->toString(acc); + acc += ", "; } - results += "}"; - // results += " having initializer: " + - // (initializer == nullptr ? "None" : initializer->toString()); - return results; + acc += "}"; } const std::vector> &getFieldTypes() { @@ -103,6 +94,36 @@ class AutumnClass : public AutumnType { } }; +class AutumnClassValue : public AutumnValue { + std::shared_ptr aclass; + +public: + AutumnClassValue(std::shared_ptr aclass) + : AutumnValue(aclass), aclass(aclass) {} + + std::string toString() const override { return aclass->toString(); } + + // Note: equal is not reflexive. Figure out if that's intended + std::shared_ptr binop_on(AutumnValue & /*rhs*/, const Token &op) override { + switch (op.type) { + case TokenType::EQUAL_EQUAL: + return std::make_shared(false); + case TokenType::BANG_EQUAL: + return std::make_shared(true); + default: + throw Error("Operator not supported on class values"); + } + } + + bool equal_to(AutumnValue & /*rhs*/) override { return false; } + + std::shared_ptr getClass() { return aclass; } + + std::shared_ptr copy() override { + return std::make_shared(aclass); + } +}; + } // namespace Autumn #endif // !__AUTUMN_CLASS_HPP__ diff --git a/include/interpreter/valuesystem/AutumnConstructor.hpp b/include/interpreter/valuesystem/AutumnConstructor.hpp index a9c1d2a..10a52e6 100644 --- a/include/interpreter/valuesystem/AutumnConstructor.hpp +++ b/include/interpreter/valuesystem/AutumnConstructor.hpp @@ -1,7 +1,6 @@ #ifndef AUTUMN_CONSTRUCTOR_HPP #define AUTUMN_CONSTRUCTOR_HPP -#include "AstPrinter.hpp" #include "AutumnCallable.hpp" #include "AutumnValue.hpp" #include "Environment.hpp" @@ -20,76 +19,45 @@ class AutumnConstructor : public AutumnCallable { std::shared_ptr closure) : declaration_(std::make_shared(*declaration)), closure_(closure) { - AstPrinter printer; - lambdaName = std::any_cast( - declaration_->accept(printer)); // Get the lambda's name + lambdaName = declaration_->prettyPrint(); } - // Returns the number of parameters the lambda expects int arity() override { return declaration_->params.size(); } - // Executes the lambda with the given arguments std::shared_ptr call(Interpreter &interpreter, const std::vector> &arguments) override { - // std::cerr << "Lambda call: " << lambdaName << std::endl; - // Create a new environment for the lambda's execution auto environment = std::make_shared(interpreter.getEnvironment(), EnvironmentType::INSTANCE); interpreter.setEnvironment(environment); - // Bind each argument to its corresponding parameter for (size_t i = 0; i < declaration_->params.size(); ++i) { try { environment->define(declaration_->params[i].lexeme, arguments[i]); } catch (const Error &e) { - throw Error("Error visiting: " + AstPrinter().print(declaration_) + + throw Error("Error visiting: " + declaration_->prettyPrint() + "\nGot: " + e.what()); } } - // Bind the new environment to the lambda's closure - // Execute the lambda's body within the new environment try { - std::shared_ptr retVal = - std::any_cast>( - declaration_->right->accept(interpreter)); - + std::shared_ptr retVal = declaration_->right->eval(interpreter); interpreter.setEnvironment(environment->getEnclosing()); return retVal; } catch (const Error &e) { - std::string argumentStrings = "("; - for (size_t i = 0; i < declaration_->params.size(); ++i) { - argumentStrings += - "{" + declaration_->params[i].lexeme + ": " + - environment->get(declaration_->params[i].lexeme)->toString() + "}"; - } - argumentStrings += ")"; - throw Error("Error calling: " + AstPrinter().print(declaration_) + - // "\nwith arguments: " + argumentStrings + "\nGot: \n" + - ": " + - e.what()); + throw Error("Error calling: " + declaration_->prettyPrint() + + ": " + e.what()); } - // std::cerr << "Lambda Res: " << retVal->toString() << std::endl; - // Restore the previous environment } - // Returns a string representation of the lambda std::string toString() const override { return std::string(lambdaName); } - // Returns a clone of the lambda - std::shared_ptr clone() override { - return std::make_shared( - std::make_shared(*declaration_), closure_); - } - private: std::string lambdaName; std::shared_ptr declaration_; std::shared_ptr closure_; - bool isInitializer_ = false; // Determines if the lambda is an initializer }; } // namespace Autumn -#endif \ No newline at end of file +#endif diff --git a/include/interpreter/valuesystem/AutumnExprValue.hpp b/include/interpreter/valuesystem/AutumnExprValue.hpp index 05afb04..1b5f066 100644 --- a/include/interpreter/valuesystem/AutumnExprValue.hpp +++ b/include/interpreter/valuesystem/AutumnExprValue.hpp @@ -13,39 +13,36 @@ namespace Autumn { class AutumnExprType : public AutumnType { - public: - static std::shared_ptr instance; + explicit AutumnExprType(SingletonKey) {} + using AutumnType::toString; - std::string toString() const override { return std::string("Expr"); } + void toString(std::string &acc) const override { acc += "Expr"; } static std::shared_ptr getInstance() { - if (instance == nullptr) { - instance = std::make_shared(); - } + static auto instance = std::make_shared(SingletonKey{}); return instance; } }; -class AutumnExprValue : public AutumnValue, - public std::enable_shared_from_this { +class AutumnExprValue : public AutumnValue { private: std::shared_ptr cexpr; std::shared_ptr cenv; public: AutumnExprValue(int instId, std::shared_ptr cexpr, std::shared_ptr cenv) - : AutumnValue(instId, cexpr, AutumnExprType::getInstance()), + : AutumnValue(instId, AutumnExprType::getInstance()), cexpr(cexpr), cenv(cenv) {} AutumnExprValue(std::shared_ptr cexpr, std::shared_ptr cenv) - : AutumnValue(cexpr, AutumnExprType::getInstance()), cexpr(cexpr), cenv(cenv) {} + : AutumnValue(AutumnExprType::getInstance()), cexpr(cexpr), cenv(cenv) {} std::string toString() const override; - bool isEqual(std::shared_ptr other) override; - std::shared_ptr clone() override { - return shared_from_this(); - } + + std::shared_ptr binop_on(AutumnValue &rhs, const Token &op) override; + bool equal_to(AutumnValue &rhs) override; + bool equal_by_expr(AutumnExprValue &lhs) override; std::shared_ptr getExpr() { return cexpr; } std::shared_ptr getCenv() { return cenv; } diff --git a/include/interpreter/valuesystem/AutumnInstance.hpp b/include/interpreter/valuesystem/AutumnInstance.hpp index e5111e7..90dba2f 100644 --- a/include/interpreter/valuesystem/AutumnInstance.hpp +++ b/include/interpreter/valuesystem/AutumnInstance.hpp @@ -5,8 +5,7 @@ #include namespace Autumn { -class AutumnInstance : public AutumnValue, - public std::enable_shared_from_this { +class AutumnInstance : public AutumnValue { private: std::shared_ptr aclass; std::unordered_map> fields; @@ -14,7 +13,7 @@ class AutumnInstance : public AutumnValue, public: AutumnInstance(int instId, std::shared_ptr aclass, std::vector> fieldvalues) - : AutumnValue(instId, std::any(), aclass), aclass(aclass) { + : AutumnValue(instId, aclass), aclass(aclass) { const std::vector &fieldnames = aclass->getFieldNames(); if (fieldnames.size() != fieldvalues.size()) { throw Error("Error initializing class " + aclass->name + @@ -39,7 +38,7 @@ class AutumnInstance : public AutumnValue, AutumnInstance(std::shared_ptr aclass, std::vector> fieldvalues) - : AutumnValue(std::any(), aclass), aclass(aclass) { + : AutumnValue(aclass), aclass(aclass) { const std::vector &fieldnames = aclass->getFieldNames(); if (fieldnames.size() != fieldvalues.size()) { throw Error("Field names and values do not match in class " + @@ -65,41 +64,51 @@ class AutumnInstance : public AutumnValue, } std::string toString() const override { - std::string result = aclass->name + "{"; + std::string s; + toString(s); + return s; + } + + void toString(std::string &acc) const override { + acc += aclass->name; + acc += "{"; int i = 0; for (const auto &key : aclass->getFieldNames()) { std::shared_ptr value = fields.at(key); if (value->getType()->toString() != - aclass->getFieldTypes()[i]->toString() && aclass->getFieldTypes()[i]->toString() != "List" - && value->getType()->toString() != "List" - ) { + aclass->getFieldTypes()[i]->toString() && + aclass->getFieldTypes()[i]->toString() != "List" && + value->getType()->toString() != "List") { throw Error("Field type mismatch in toString: " + key + " with type " + aclass->getFieldTypes()[i]->toString() + " vs " + value->toString() + " in " + aclass->name); } - std::string valueString = value->toString(); - // for each line, add a tab - std::string tabbedValueString = ""; - for (const auto &line : valueString) { - tabbedValueString += line; - if (line == '\n') { - tabbedValueString += "\t"; - } + if (i != 0) acc += ","; + acc += key; + acc += ": "; + // Indent nested content: emit the value into a temp buffer, then + // copy it over with a '\t' appended after every newline. + std::string child; + value->toString(child); + for (char c : child) { + acc += c; + if (c == '\n') acc += '\t'; } - result += (i == 0 ? "" : ",") + key + ": " + tabbedValueString; i++; } if (fields.size() == 0) { - result += "None"; + acc += "None"; } if (aclass->methods.size() != 0) { - result += "\tMethods: "; + acc += "\tMethods: "; for (const auto &method : aclass->methods) { - result += "\n\t" + method.first + ": " + method.second->toString(); + acc += "\n\t"; + acc += method.first; + acc += ": "; + acc += method.second->toString(); } } - result += "}"; - return result; + acc += "}"; } static std::string normalizeName(const std::string &name) { @@ -112,33 +121,37 @@ class AutumnInstance : public AutumnValue, std::string getClassName() { return aclass->name; } + // Field-only lookup. Throws if `name` is not a field on this instance. + // For call sites that also need method binding, use getWithMethod(self, name). std::shared_ptr get(const std::string &name) { std::string normalizedName = normalizeName(name); - if (fields.find(normalizedName) != fields.end()) { - // std::cerr << "Field found: " << normalizedName << std::endl; - return fields[normalizedName]; + auto it = fields.find(normalizedName); + if (it != fields.end()) { + return it->second; } - /// Currently Autumn does not allow method call in class - try { - std::shared_ptr method = aclass->findMethod(normalizedName); - if (method != nullptr) { - method->setInstance(shared_from_this()); - // std::cerr << "Method found: " << std::endl; - // std::cerr << method->toString() << std::endl; - auto pCallableValue = - std::dynamic_pointer_cast(method); - // std::cerr << "pCallableValue = " << pCallableValue->toString() - // << std::endl; - return pCallableValue; - } else { - throw Error("Undefined property '" + name + "' for class " + - aclass->name); - } - } catch (Error &e) { - throw Error("Undefined property '" + name + "' for class " + - aclass->name + " - " + e.what()); + throw Error("Undefined property '" + name + "' for class " + + aclass->name); + } + + // Field-or-method lookup. `self` must be a shared_ptr owning this instance; + // it is used to bind any matching method. Takes self as a parameter so we + // don't need `enable_shared_from_this`. + static std::shared_ptr + getWithMethod(const std::shared_ptr &self, + const std::string &name) { + std::string normalizedName = normalizeName(name); + auto it = self->fields.find(normalizedName); + if (it != self->fields.end()) { + return it->second; + } + std::shared_ptr method = + self->aclass->findMethod(normalizedName); + if (method != nullptr) { + method->setInstance(self); + return std::dynamic_pointer_cast(method); } - return nullptr; + throw Error("Undefined property '" + name + "' for class " + + self->aclass->name); } void set(const std::string &name, std::shared_ptr value) { @@ -156,43 +169,38 @@ class AutumnInstance : public AutumnValue, "', all fields: " + aclass->toString()); } - bool isEqual(std::shared_ptr other) override { - std::shared_ptr otherInstance = - std::dynamic_pointer_cast(other); - if (otherInstance == nullptr) { - return false; + std::shared_ptr binop_on(AutumnValue &rhs, const Token &op) override { + switch (op.type) { + case TokenType::EQUAL_EQUAL: + return std::make_shared(equal_to(rhs)); + case TokenType::BANG_EQUAL: + return std::make_shared(!equal_to(rhs)); + default: + throw Error("Operator not supported on instances"); } - // Else, check matching in terms of fields - if (fields.size() != otherInstance->fields.size()) { - return false; - } - for (const auto &[key, value] : fields) { - if (otherInstance->fields.find(key) == otherInstance->fields.end()) { - return false; - } - if (!value->isEqual(otherInstance->fields[key])) { - return false; + } + + bool equal_to(AutumnValue &rhs) override { return rhs.equal_by_instance(*this); } + + bool equal_by_instance(AutumnInstance &lhs) override { + if (lhs.fields.size() != fields.size()) return false; + for (const auto &[key, value] : lhs.fields) { + auto it = fields.find(key); + if (it == fields.end()) return false; + if (!value) { + if (it->second) return false; + continue; } + if (!value->isEqual(it->second)) return false; } return true; } - std::shared_ptr clone() override { - // TODO: Check if this breaks things. - // std::vector> fieldvalues; - // fieldvalues.reserve(fields.size()); - // for (auto &key : aclass->getFieldNames()) { - // fieldvalues.push_back(fields[key]->clone()); - // } - // return std::make_shared(instId, aclass, fieldvalues); - return shared_from_this(); - } - std::shared_ptr copy() override { std::vector> fieldvalues; fieldvalues.reserve(fields.size()); for (auto &key : aclass->getFieldNames()) { - fieldvalues.push_back(fields[key]->clone()); + fieldvalues.push_back(fields[key]); } return std::make_shared(aclass, fieldvalues); diff --git a/include/interpreter/valuesystem/AutumnLambda.hpp b/include/interpreter/valuesystem/AutumnLambda.hpp index aa23ceb..fb96060 100644 --- a/include/interpreter/valuesystem/AutumnLambda.hpp +++ b/include/interpreter/valuesystem/AutumnLambda.hpp @@ -1,7 +1,6 @@ #ifndef AUTUMN_LAMBDA_HPP #define AUTUMN_LAMBDA_HPP -#include "AstPrinter.hpp" #include "AutumnCallable.hpp" #include "AutumnValue.hpp" #include "Environment.hpp" @@ -14,80 +13,49 @@ namespace Autumn { -class AutumnLambda : public AutumnCallable, - public std::enable_shared_from_this { +class AutumnLambda : public AutumnCallable { public: - AutumnLambda(std::shared_ptr declaration, + AutumnLambda(Lambda &declaration, std::shared_ptr closure) - : declaration_(std::make_shared(*declaration)), + : declaration_(std::make_unique(declaration)), closure_(closure) { - AstPrinter printer; - lambdaName = std::any_cast( - declaration_->accept(printer)); // Get the lambda's name + lambdaName = declaration_->prettyPrint(); } - // Returns the number of parameters the lambda expects int arity() override { return declaration_->params.size(); } - // Executes the lambda with the given arguments std::shared_ptr call(Interpreter &interpreter, const std::vector> &arguments) override { - // std::cerr << "Lambda call: " << lambdaName << std::endl; - // Create a new environment for the lambda's execution auto environment = std::make_shared(interpreter.getEnvironment()); interpreter.setEnvironment(environment); - // Bind each argument to its corresponding parameter for (size_t i = 0; i < declaration_->params.size(); ++i) { try { environment->define(declaration_->params[i].lexeme, arguments[i]); } catch (const Error &e) { - throw Error("Error visiting: " + AstPrinter().print(declaration_) + + throw Error("Error visiting: " + declaration_->prettyPrint() + "\nGot: " + e.what()); } } - // Bind the new environment to the lambda's closure - // Execute the lambda's body within the new environment try { - std::shared_ptr retVal = - std::any_cast>( - declaration_->right->accept(interpreter)); - + std::shared_ptr retVal = declaration_->right->eval(interpreter); interpreter.setEnvironment(environment->getEnclosing()); return retVal; } catch (const Error &e) { - std::string argumentStrings = "("; - for (size_t i = 0; i < declaration_->params.size(); ++i) { - argumentStrings += - "{" + declaration_->params[i].lexeme + ": " + - environment->get(declaration_->params[i].lexeme)->toString() + "}"; - } - argumentStrings += ")"; - throw Error("Error calling: " + AstPrinter().print(declaration_) + - // "\nwith arguments: " + argumentStrings + "\nGot: \n" + - ": " + - e.what()); + throw Error("Error calling: " + declaration_->prettyPrint() + + ": " + e.what()); } - // std::cerr << "Lambda Res: " << retVal->toString() << std::endl; - // Restore the previous environment } - // Returns a string representation of the lambda std::string toString() const override { return std::string(lambdaName); } - // Returns a clone of the lambda - std::shared_ptr clone() override { - return shared_from_this(); - } - private: std::string lambdaName; std::shared_ptr declaration_; std::shared_ptr closure_; - bool isInitializer_ = false; // Determines if the lambda is an initializer }; } // namespace Autumn diff --git a/include/interpreter/valuesystem/AutumnValue.hpp b/include/interpreter/valuesystem/AutumnValue.hpp index 87f87a5..c8fdf2b 100644 --- a/include/interpreter/valuesystem/AutumnValue.hpp +++ b/include/interpreter/valuesystem/AutumnValue.hpp @@ -3,163 +3,192 @@ #include "AutumnType.hpp" #include "Error.hpp" -#include -#include #include +#include #include +#include +#include #include namespace Autumn { - /* // TODO: THIS IS A MUCH BIGGER OPTIMIZATION, beware of going down this path -template -class ValueVariant { - std::variant data; -public: - template - T& get() { return std::get(data); } -}; -using AutumnValueVariant = ValueVariant< - int, // Number - std::string, // String - bool, // Bool - std::vector>, // List - std::nullptr_t // Null +class Interpreter; +class AutumnInstance; +class AutumnExprValue; +class AutumnCallableValue; +class AutumnValue; + +// Non-owning view over the primitive payload of an AutumnValue. +// Used by stdlib introspection (IsOutsideBounds, isFreePos, etc.). Callers +// must not retain the view beyond the lifetime of the source value. +using AutumnValueData = std::variant< + int, + bool, + std::string_view, + std::span> >; -// TODO: Implement this with Autumn Values -*/ class AutumnValue { static int instCount; protected: int instId; - AutumnValue(int instId, std::any value, std::shared_ptr type) - : instId(instId), value(value), type(type) {} + AutumnValue(int instId, std::shared_ptr type) + : instId(instId), type(type) {} - std::any value; std::shared_ptr type; public: - AutumnValue(std::any value, std::shared_ptr type) - : value(value), type(type) { + AutumnValue(std::shared_ptr type) : type(type) { instId = instCount++; - } // This is for inner class definition + } + virtual std::string toString() const = 0; - virtual bool isEqual(std::shared_ptr other) { - return false; - } // By default, return false + virtual void toString(std::string &acc) const { acc += toString(); } + + // Binary operator dispatch (visitor-style double dispatch). See the + // refactor notes in AutumnValue.cpp for the full method matrix. + virtual std::shared_ptr binop_on(AutumnValue &rhs, const Token &op) = 0; + virtual bool equal_to(AutumnValue &rhs) = 0; + + virtual int add_by_number(int lhs) { throw Error("Binary + requires two numbers"); } + virtual int sub_by_number(int lhs) { throw Error("Binary - requires two numbers"); } + virtual int mul_by_number(int lhs) { throw Error("Binary * requires two numbers"); } + virtual int div_by_number(int lhs) { throw Error("Binary / requires two numbers"); } + virtual int mod_by_number(int lhs) { throw Error("Binary % requires two numbers"); } + + virtual bool greater_by_number(int lhs) { throw Error("Binary > requires two numbers"); } + virtual bool greater_equal_by_number(int lhs) { throw Error("Binary >= requires two numbers"); } + virtual bool less_by_number(int lhs) { throw Error("Binary < requires two numbers"); } + virtual bool less_equal_by_number(int lhs) { throw Error("Binary <= requires two numbers"); } + + virtual bool equal_by_number(int /*lhs*/) { return false; } + virtual bool equal_by_bool(bool /*lhs*/) { return false; } + virtual bool equal_by_string(const std::string & /*lhs*/) { return false; } + virtual bool equal_by_list(const std::vector> & /*lhs*/) { return false; } + virtual bool equal_by_instance(AutumnInstance & /*lhs*/) { return false; } + virtual bool equal_by_expr(AutumnExprValue & /*lhs*/) { return false; } + virtual bool equal_by_callable(AutumnCallableValue & /*lhs*/) { return false; } + + // Thin wrapper kept for legacy callers. Funnels through equal_to. + bool isEqual(std::shared_ptr other) { + if (!other) return false; + return equal_to(*other); + } + + virtual std::shared_ptr eval_unary(const Token &op); virtual ~AutumnValue() = default; virtual bool isTruthy() { return true; } - virtual std::shared_ptr clone() = 0; int getInstId() { return instId; } void setInstId(int instId) { this->instId = instId; } - std::any getValue() { return value; } - void setValue(std::any value) { this->value = value; } - // copy + + // Primitive-value introspection for stdlib. Default throws — only the + // four primitive subclasses override. + virtual AutumnValueData getValue() { + throw Error("getValue() not supported on this AutumnValue kind"); + } + virtual std::shared_ptr copy() = 0; std::shared_ptr getType() { return type; } }; -class AutumnNumber final: public AutumnValue, - public std::enable_shared_from_this { +class AutumnNumber final : public AutumnValue { + int value; public: AutumnNumber(int instId, int value) - : AutumnValue(instId, value, AutumnNumberType::getInstance()) {} + : AutumnValue(instId, AutumnNumberType::getInstance()), value(value) {} AutumnNumber(int value) - : AutumnValue(value, AutumnNumberType::getInstance()) {} + : AutumnValue(AutumnNumberType::getInstance()), value(value) {} + std::string toString() const override { - return "(" + std::to_string(std::any_cast(value)) + ": N)"; - } - bool isEqual(std::shared_ptr other) override; - std::shared_ptr clone() override { - return shared_from_this(); + return "(" + std::to_string(value) + ": N)"; } - int getNumber() const { return std::any_cast(value); } - AutumnNumber operator+(const AutumnNumber &other) const { - return AutumnNumber(getNumber() + other.getNumber()); - } + AutumnValueData getValue() override { return value; } + int getNumber() const { return value; } - AutumnNumber operator-(const AutumnNumber &other) const { - return AutumnNumber(getNumber() - other.getNumber()); - } + std::shared_ptr eval_unary(const Token &op) override; + std::shared_ptr binop_on(AutumnValue &rhs, const Token &op) override; + bool equal_to(AutumnValue &rhs) override; - AutumnNumber operator*(const AutumnNumber &other) const { - return AutumnNumber(getNumber() * other.getNumber()); - } + int add_by_number(int lhs) override; + int sub_by_number(int lhs) override; + int mul_by_number(int lhs) override; + int div_by_number(int lhs) override; + int mod_by_number(int lhs) override; - AutumnNumber operator/(const AutumnNumber &other) const { - return AutumnNumber(getNumber() / other.getNumber()); - } + bool greater_by_number(int lhs) override; + bool greater_equal_by_number(int lhs) override; + bool less_by_number(int lhs) override; + bool less_equal_by_number(int lhs) override; + + bool equal_by_number(int lhs) override; std::shared_ptr copy() override { - return std::make_shared(std::any_cast(value)); + return std::make_shared(value); } }; -; -class AutumnString final: public AutumnValue, - public std::enable_shared_from_this { +class AutumnString final : public AutumnValue { + std::string value; public: AutumnString(int instId, std::string value) - : AutumnValue(instId, value, AutumnStringType::getInstance()) {} + : AutumnValue(instId, AutumnStringType::getInstance()), value(std::move(value)) {} AutumnString(std::string value) - : AutumnValue(value, AutumnStringType::getInstance()) {} - std::string toString() const override { - return "(" + std::any_cast(value) + ": S)"; - } + : AutumnValue(AutumnStringType::getInstance()), value(std::move(value)) {} - bool isEqual(std::shared_ptr other) override; - std::shared_ptr clone() override { - return shared_from_this(); + std::string toString() const override { + return "(" + value + ": S)"; } - std::string getString() const { return std::any_cast(value); } - AutumnString operator+(const AutumnString &other) const { - return AutumnString(getString() + other.getString()); - } + AutumnValueData getValue() override { return std::string_view(value); } + const std::string &getString() const { return value; } - AutumnString(AutumnString &&other) - : AutumnValue(std::string(other.getString()), - AutumnStringType::getInstance()) {} + std::shared_ptr binop_on(AutumnValue &rhs, const Token &op) override; + bool equal_to(AutumnValue &rhs) override; + bool equal_by_string(const std::string &lhs) override; std::shared_ptr copy() override { - return std::make_shared( - std::string(std::any_cast(value))); + return std::make_shared(value); } }; -class AutumnBool final: public AutumnValue, - public std::enable_shared_from_this { +class AutumnBool final : public AutumnValue { + bool value; + public: - AutumnBool(int instId, std::any value) - : AutumnValue(instId, value, AutumnBoolType::getInstance()) {} - AutumnBool(std::any value) - : AutumnValue(value, AutumnBoolType::getInstance()) {} + AutumnBool(int instId, bool value) + : AutumnValue(instId, AutumnBoolType::getInstance()), value(value) {} + AutumnBool(bool value) + : AutumnValue(AutumnBoolType::getInstance()), value(value) {} + std::string toString() const override { - return std::string("(") + (std::any_cast(value) ? "true" : "false") + - ": " + type->toString() + ")"; + return std::string("(") + (value ? "true" : "false") + ": " + type->toString() + ")"; } - bool isEqual(std::shared_ptr other) override; - bool isTruthy() override { return std::any_cast(value); } + bool isTruthy() override { return value; } - std::shared_ptr clone() override { - return shared_from_this(); - } + AutumnValueData getValue() override { return value; } + bool getBool() const { return value; } - bool getBool() const { return std::any_cast(value); } - AutumnBool(AutumnBool &other) : AutumnValue(other) {} + std::shared_ptr eval_unary(const Token &op) override; + + std::shared_ptr binop_on(AutumnValue &rhs, const Token &op) override; + bool equal_to(AutumnValue &rhs) override; + bool equal_by_bool(bool lhs) override; + // Legacy coercion: number lhs equals bool rhs iff lhs == (value ? 1 : 0). + // Reverse (bool == number) is not coerced. + bool equal_by_number(int lhs) override; std::shared_ptr copy() override { return std::make_shared(value); } }; -class AutumnList final: public AutumnValue, - public std::enable_shared_from_this { +class AutumnList final : public AutumnValue { + std::shared_ptr>> values; + public: static std::shared_ptr inferType(const std::vector> &values) { @@ -179,116 +208,72 @@ class AutumnList final: public AutumnValue, AutumnList(int instId, std::shared_ptr>> values) - : AutumnValue(instId, values, inferType(*values)) {} + : AutumnValue(instId, inferType(*values)), values(std::move(values)) {} AutumnList(std::shared_ptr>> values) - : AutumnValue(values, inferType(*values)) {} + : AutumnValue(inferType(*values)), values(std::move(values)) {} AutumnList() - : AutumnValue(std::make_shared>>( - std::vector>({})), - AutumnListType::getInstance()) {} + : AutumnValue(AutumnListType::getInstance()), + values(std::make_shared>>()) {} + + AutumnValueData getValue() override { + return std::span>(*values); + } + + std::shared_ptr binop_on(AutumnValue &rhs, const Token &op) override; + bool equal_to(AutumnValue &rhs) override; + bool equal_by_list(const std::vector> &lhs) override; std::string toString() const override { - std::string result = "(["; - std::shared_ptr>> plist; - try { - plist = std::any_cast< - std::shared_ptr>>>(value); - result.reserve(2 + (plist->size() * 20) + (plist->size() - 1) * 2 + 3 + type->toString().length()); - } catch (const std::bad_any_cast &e) { - std::cerr << "Error in toString(): bad_any_cast for 'value'." - << std::endl; - return "([Invalid List])"; - } + std::string s; + toString(s); + return s; + } - if (!plist) { - std::cerr << "Error in toString(): 'plist' is null." << std::endl; - return "([Null List])"; + void toString(std::string &acc) const override { + if (!values) { + acc += "([Null List])"; + return; } - - for (size_t i = 0; i < plist->size(); i++) { - auto &element = (*plist)[i]; + acc += "(["; + for (size_t i = 0; i < values->size(); i++) { + auto &element = (*values)[i]; if (!element) { - std::cerr << "WARNING: Element at index " << i << " is null." - << std::endl; - result += "null"; + acc += "null"; } else { try { - result += element->toString(); + element->toString(acc); } catch (const std::exception &e) { - std::cerr << "Error in element->toString() at index " << i << ": " - << e.what() << std::endl; - result += "error"; + acc += "error"; } } - if (i != plist->size() - 1) { - result += ", "; - } + if (i != values->size() - 1) acc += ", "; } - result += "] :" + (type ? type->toString() : "Unknown Type") + ")"; - return result; - } - bool isEqual(std::shared_ptr other) override; - - std::shared_ptr clone() override { - return shared_from_this(); + acc += "] :"; + if (type) type->toString(acc); + else acc += "Unknown Type"; + acc += ")"; } std::shared_ptr>> getValues() { - try { - return std::any_cast< - std::shared_ptr>>>(value); - } catch (std::bad_any_cast &e) { - std::cerr << "AutumnList::getValues Error: " << e.what() << std::endl; - throw e; - } + return values; } void add(std::shared_ptr elem); + bool isTruthy() override { - // If value is nullptr, it's also false - if (value.has_value() == false) { - return false; - } - if (std::any_cast< - std::shared_ptr>>>(value) - ->size() == 0) { - return false; - } - return true; + return values && !values->empty(); } std::shared_ptr copy() override { - auto pList = std::any_cast< - std::shared_ptr>>>(value); std::vector> newlist; - newlist.reserve(pList->size()); - for (size_t i = 0; i < pList->size(); i++) { - newlist.push_back((*pList)[i]->copy()); + newlist.reserve(values->size()); + for (const auto &v : *values) { + newlist.push_back(v->copy()); } - auto pNewList = - std::make_shared>>(newlist); - return std::make_shared(pNewList); - } -}; - -class AutumnNull final: public AutumnValue, - public std::enable_shared_from_this { -public: - AutumnNull(int instId, std::any value) - : AutumnValue(instId, value, AutumnUnknownType::getInstance()) {} - AutumnNull(std::any value) - : AutumnValue(value, AutumnUnknownType::getInstance()) {} - std::string toString() const override { return "null"; } - bool isEqual(std::shared_ptr other) override; - bool isTruthy() override { return false; } - std::shared_ptr clone() override { - return shared_from_this(); - } - - std::shared_ptr copy() override { - return std::make_shared(value); + return std::make_shared( + std::make_shared>>(std::move(newlist))); } }; diff --git a/include/parser/AstPrinter.hpp b/include/parser/AstPrinter.hpp index ce82c38..595a510 100644 --- a/include/parser/AstPrinter.hpp +++ b/include/parser/AstPrinter.hpp @@ -1,253 +1,7 @@ #ifndef _AUTUMN_ASTPRINTER_HPP_ #define _AUTUMN_ASTPRINTER_HPP_ -#include "Error.hpp" -#include "Expr.hpp" -#include "Stmt.hpp" -#include "Token.hpp" -#include -#include -#include -#include -#include -namespace Autumn { -class AstPrinter : public Expr::Visitor, public Stmt::Visitor { - std::string parenthesize(const std::string &name, - const std::vector &expr_strs) { - std::string result = "(" + name; - for (const auto &expr_s : expr_strs) { - result += " "; - result += expr_s; - } - result += ")"; - return result; - } - -public: - std::string print(const std::shared_ptr &expr) { - return std::any_cast(expr->accept(*this)); - } - - std::string print(const std::shared_ptr &stmt) { - return std::any_cast(stmt->accept(*this)); - } - - std::any visitAssignExpr(std::shared_ptr expr) override { - try { - std::string valueString = - std::any_cast(expr->value->accept(*this)); - return parenthesize(" " + expr->name.lexeme, {valueString}); - } catch (const std::bad_any_cast &e) { - throw Error("Assign: Unknown type"); - } - } - - std::any visitBinaryExpr(std::shared_ptr expr) override { - std::string leftString = - std::any_cast(expr->left->accept(*this)); - std::string rightString = - std::any_cast(expr->right->accept(*this)); - return parenthesize(expr->op.lexeme, {leftString, rightString}); - } - - std::any visitCallExpr(std::shared_ptr expr) override { - std::string calleeString = - std::any_cast(expr->callee->accept(*this)); - std::vector arguments; - for (const auto &argument : expr->arguments) { - arguments.push_back(std::any_cast(argument->accept(*this))); - } - return parenthesize(" " + calleeString, arguments); - } - - std::any visitGetExpr(std::shared_ptr expr) override { - std::string objectString = - std::any_cast(expr->object->accept(*this)); - return parenthesize("" + expr->name.lexeme, {objectString}); - } - - std::any visitLiteralExpr(std::shared_ptr expr) override { - // Check if literal is string, boolean or int - try { - std::any_cast(expr->value); - return "(NUMBER " + std::to_string(std::any_cast(expr->value)) + ")"; - } catch (const std::bad_any_cast &e) { - try { - std::any_cast(expr->value); - return std::any_cast(expr->value) ? std::string("(BOOL true)") - : std::string("(BOOL false)"); - } catch (const std::bad_any_cast &e) { - try { - std::any_cast(expr->value); - return "(STRING " + std::any_cast(expr->value) + ")"; - } catch (const std::bad_any_cast &e) { - throw Error("Unknown literal type"); - } - } - } - return std::to_string(std::any_cast(expr->value)); - } - - std::any visitGroupingExpr(std::shared_ptr expr) override { - std::string expressionString = - std::any_cast(expr->expression->accept(*this)); - return parenthesize("group", {expressionString}); - } - - std::any visitUnaryExpr(std::shared_ptr expr) override { - std::string rightString = - std::any_cast(expr->right->accept(*this)); - return parenthesize(expr->op.lexeme, {rightString}); - } - - std::any visitVariableExpr(std::shared_ptr expr) override { - return "(var " + expr->name.lexeme + ")"; - } - - std::any visitLogicalExpr(std::shared_ptr expr) override { - std::string leftString = - std::any_cast(expr->left->accept(*this)); - std::string rightString = - std::any_cast(expr->right->accept(*this)); - return parenthesize(expr->op.lexeme, {leftString, rightString}); - } - - std::any visitSetExpr(std::shared_ptr expr) override { - std::string objectString = - std::any_cast(expr->object->accept(*this)); - std::string valueString = - std::any_cast(expr->value->accept(*this)); - return parenthesize("" + expr->name.lexeme, - {objectString, valueString}); - } - - std::any visitLambdaExpr(std::shared_ptr expr) override { - std::vector params; - for (const auto ¶m : expr->params) { - params.push_back(param.lexeme); - } - std::string paramsString = "("; - for (const auto ¶m : params) { - paramsString += param + " "; - } - paramsString += ")"; - std::string rightString = - std::any_cast(expr->right->accept(*this)); - return parenthesize("lambda", {paramsString, rightString}); - } - - std::any visitTypeVariableExpr(std::shared_ptr expr) override { - std::string nameString = expr->name.lexeme; - return "( " + nameString + ")"; - } - - std::any visitTypeDeclExpr(std::shared_ptr expr) override { - std::string nameString = expr->name.lexeme; - std::string typeString = - std::any_cast(expr->typeexpr->accept(*this)); - return "( " + nameString + " " + typeString + ")"; - } - - std::any visitListTypeExprExpr(std::shared_ptr expr) override { - std::string typeString = - std::any_cast(expr->typeexpr->accept(*this)); - return "( " + typeString + ")"; - } - - std::any visitListVarExprExpr(std::shared_ptr expr) override { - std::vector varExprs; - for (const auto &varExpr : expr->varExprs) { - varExprs.push_back(std::any_cast(varExpr->accept(*this))); - } - std::string result = "["; - for (const auto &varExpr : varExprs) { - result += varExpr; - } - result += "]"; - return result; - } - - std::any visitIfExprExpr(std::shared_ptr expr) override { - std::string conditionString = - std::any_cast(expr->condition->accept(*this)); - std::string thenBranchString = - std::any_cast(expr->thenBranch->accept(*this)); - std::string elseBranchString = - std::any_cast(expr->elseBranch->accept(*this)); - return parenthesize("if", - {conditionString, thenBranchString, elseBranchString}); - } - - std::any visitLetExpr(std::shared_ptr expr) override { - std::vector subExprs; - for (const auto &subexpr : expr->exprs) { - subExprs.push_back(std::any_cast(subexpr->accept(*this))); - } - std::string result = "{"; - for (const auto &varExpr : subExprs) { - result += varExpr; - } - result += "}"; - return result; - } - - std::any visitInitNextExpr(std::shared_ptr expr) override { - try { - std::string initializerString = - std::any_cast(expr->initializer->accept(*this)); - std::string nextString = - std::any_cast(expr->nextExpr->accept(*this)); - return parenthesize("initnext", {initializerString, nextString}); - } catch (const std::bad_any_cast &e) { - throw Error("InitNext: Unknown type"); - } - } - - std::any visitExpressionStmt(std::shared_ptr stmt) override { - try { - return std::any_cast(stmt->expression->accept(*this)); - } catch (const std::bad_any_cast &e) { - throw Error("visitExpressionStmt: bad_any_cast"); - } - } - - std::any visitBlockStmt(std::shared_ptr stmt) override { - std::vector statements; - for (const auto &statement : stmt->statements) { - statements.push_back( - std::any_cast(statement->accept(*this))); - } - std::string result = ""; - for (const auto &statement : statements) { - result += statement; - } - return "{" + result + "}"; - } - - std::any visitObjectStmt(std::shared_ptr stmt) override { - std::vector fields; - for (const auto &field : stmt->fields) { - fields.push_back(std::any_cast(field->accept(*this))); - } - std::string result = ":("; - for (const auto &field : fields) { - result += field; - } - result += ")"; - std::string cellString = - std::any_cast(stmt->Cell->accept(*this)); - return "( " + stmt->name.lexeme + " " + result + " " + cellString + - ")"; - } - - std::any visitOnStmtStmt(std::shared_ptr stmt) override { - std::string conditionString = - std::any_cast(stmt->condition->accept(*this)); - std::string exprString = - std::any_cast(stmt->expr->accept(*this)); - return "(on " + conditionString + ": " + exprString + ")"; - } -}; -} // namespace Autumn +// AstPrinter functionality is now provided by Expr::prettyPrint() virtual method. +// This header is kept for backward compatibility with existing includes. #endif diff --git a/include/parser/Parser.hpp b/include/parser/Parser.hpp index ce0490e..d11a15b 100644 --- a/include/parser/Parser.hpp +++ b/include/parser/Parser.hpp @@ -3,6 +3,7 @@ #include "AutumnStdLib.hpp" #include "Expr.hpp" +#include "Interner.hpp" #include "Lexer.hpp" #include "Stmt.hpp" #include "Token.hpp" @@ -18,6 +19,14 @@ class SExpParser { const std::string &source; int current = 0; + // Interner used to create stable Symbol* handles for identifiers. + // Either external (borrowed, set via the two-arg constructor) or owned + // (default-constructed). Callers that feed ASTs to an Interpreter must + // pass the Interpreter's interner so Symbol identity is consistent + // across stdlib and user-program parses. + Interner ownedInterner_; + Interner *interner_ = &ownedInterner_; + bool isBinaryOp(const TokenType &op) { return op == TokenType::PLUS || op == TokenType::MINUS || @@ -50,6 +59,10 @@ class SExpParser { public: SExpParser(const std::string &source) : source(source) {} + SExpParser(const std::string &source, Interner &interner) + : source(source), interner_(&interner) {} + + Interner &getInterner() { return *interner_; } std::shared_ptr parseTypeExpr(std::shared_ptr sexp, int line = -1) { @@ -74,8 +87,10 @@ class SExpParser { if (tok.lexeme == "List") { return std::make_shared(parseTypeExpr(sexp->getChild(1))); } - return std::make_shared(Token( - TokenType::IDENTIFIER, head->getString(), head->getString(), line)); + return std::make_shared( + Token(TokenType::IDENTIFIER, head->getString(), head->getString(), + line), + interner_->intern(head->getString())); } else { throw std::runtime_error("ParseError: Invalid type expression"); } @@ -92,7 +107,7 @@ class SExpParser { } return std::make_shared( Token(tok.type, tok.lexeme, tok.literal, line), - parseTypeExpr(sexp->getChild(2))); + interner_->intern(tok.lexeme), parseTypeExpr(sexp->getChild(2))); } std::shared_ptr parseIfExpr(std::shared_ptr sexp, @@ -136,18 +151,19 @@ class SExpParser { throw std::runtime_error("ParseError: Left hand side must be a variable"); } if (var != nullptr) { - return std::make_shared(var->name, rhs); + return std::make_shared(var->name, var->nameId, rhs); } else { - return std::make_shared(get->object, get->name, rhs); + return std::make_shared(get->object, get->name, get->nameId, rhs); } } std::shared_ptr parseGetExpr(std::shared_ptr sexp, int line) { - return std::make_shared(parseExpr(sexp->getChild(1), line), - Token(TokenType::IDENTIFIER, - sexp->getChild(2)->getString(), - sexp->getChild(2)->getString(), line)); + return std::make_shared( + parseExpr(sexp->getChild(1), line), + Token(TokenType::IDENTIFIER, sexp->getChild(2)->getString(), + sexp->getChild(2)->getString(), line), + interner_->intern(sexp->getChild(2)->getString())); } std::shared_ptr parseListVarExpr(std::shared_ptr sexp, @@ -166,7 +182,7 @@ class SExpParser { int num = std::stoi(toks[1].lexeme); return std::make_shared( Token(toks[0].type, toks[0].lexeme, toks[0].literal, line), - std::make_shared(num)); + std::make_shared(num)); } auto tok = Lexer(sexp->getChild(0)->getString()).scanTokens()[0]; return std::make_shared( @@ -177,6 +193,7 @@ class SExpParser { std::shared_ptr parseFunction(std::shared_ptr sexp, int line) { std::vector params; + std::vector paramIds; auto child1 = sexp->getChild(1); if (!child1->isSexp() || child1->childCount() == 0) { Token param = Lexer(child1->getString()).scanTokens()[0]; @@ -186,7 +203,8 @@ class SExpParser { sexp->toString()); } if (param.type == TokenType::EOF_TOKEN) { // Empty function - return std::make_shared(params, parseExpr(sexp->getChild(2))); + return std::make_shared(params, paramIds, + parseExpr(sexp->getChild(2))); } if (param.type != TokenType::IDENTIFIER) { throw std::runtime_error("ParseError: Function parameter must be an " @@ -194,6 +212,7 @@ class SExpParser { TokenTypeToString(param.type)); } params.push_back(Token(param.type, param.lexeme, param.literal, line)); + paramIds.push_back(interner_->intern(param.lexeme)); } else { for (int i = 0; i < child1->childCount(); i++) { auto tok = Lexer(child1->getChild(i)->getString()).scanTokens()[0]; @@ -204,9 +223,11 @@ class SExpParser { TokenTypeToString(tok.type)); } params.push_back(tok); + paramIds.push_back(interner_->intern(tok.lexeme)); } } - return std::make_shared(params, parseExpr(sexp->getChild(2), line)); + return std::make_shared(params, paramIds, + parseExpr(sexp->getChild(2), line)); } std::shared_ptr parseLetExpr(std::shared_ptr sexp, @@ -250,29 +271,31 @@ class SExpParser { } else if (tok.type == TokenType::DOTDOT) { return parseGetExpr(sexp, line); } else if (tok.type == TokenType::NUMBER) { - return std::make_shared(std::stoi(tok.lexeme)); + return std::make_shared(std::stoi(tok.lexeme)); } else if (tok.type == TokenType::STRING) { - return std::make_shared(tok.lexeme); + return std::make_shared(tok.lexeme); } else if (tok.type == TokenType::FUN) { return parseFunction(sexp, line); } else if (tok.type == TokenType::MAPTO) { return parseFunction(sexp, line); } else if (tok.type == TokenType::TRUE) { - return std::make_shared(true); + return std::make_shared(true); } else if (tok.type == TokenType::FALSE) { - return std::make_shared(false); + return std::make_shared(false); } else if (tok.type == TokenType::IDENTIFIER) { if (tok.lexeme == "list") { return parseListVarExpr(sexp, line); } else { + Symbol nameId = interner_->intern(tok.lexeme); if (sexp->childCount() == 1) { - return std::make_shared(tok); + return std::make_shared(tok, nameId); } std::vector> args; for (int i = 1; i < sexp->childCount(); i++) { args.push_back(parseExpr(sexp->getChild(i), line)); } - return std::make_shared(std::make_shared(tok), args); + return std::make_shared( + std::make_shared(tok, nameId), args); } } else { throw std::runtime_error("ParseError: Invalid expression" + @@ -326,10 +349,10 @@ class SExpParser { if (cexpr == nullptr) { throw std::runtime_error("ParseError: Cell field is required"); } - return std::make_shared(Token(TokenType::IDENTIFIER, - name->getString(), name->getString(), - line), - fields, cexpr); + return std::make_shared( + Token(TokenType::IDENTIFIER, name->getString(), name->getString(), + line), + interner_->intern(name->getString()), fields, cexpr); } std::shared_ptr parseOnStmt(std::shared_ptr sexp, diff --git a/include/static_analyzer/StaticAnalyzer.hpp b/include/static_analyzer/StaticAnalyzer.hpp deleted file mode 100644 index b41baf4..0000000 --- a/include/static_analyzer/StaticAnalyzer.hpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "AutumnCallableValue.hpp" -#include "Interpreter.hpp" -#include - -namespace Autumn { - class FunctionCollector: public Expr::Visitor, public Stmt::Visitor, public std::enable_shared_from_this { - public: - std::shared_ptr interpreter; - - FunctionCollector(std::shared_ptr interpreter) : interpreter(interpreter) {} // Assuming the interpreter is already initialized and ran through the script - - std::vector> collectFunctions(std::shared_ptr stmt) { - std::vector> functions; - stmt->accept(*this); - return functions; - } - - std::any visitLiteralExpr(std::shared_ptr expr) override { - return std::make_shared>>(); - } - - std::any visitGroupingExpr(std::shared_ptr expr) override { - throw Error("Grouping expression is not supposed to be used in this language"); - } - - std::any visitUnaryExpr(std::shared_ptr expr) override { - auto res_right = expr->right->accept(*this); - return res_right; - } - - std::any visitBinaryExpr(std::shared_ptr expr) override { - auto res_left = expr->left->accept(*this); - auto res_right = expr->right->accept(*this); - std::shared_ptr>> res_left_vector = std::any_cast>>>(res_left); - std::shared_ptr>> res_right_vector = std::any_cast>>>(res_right); - // Combine the two vectors - std::vector> combined_vector; - combined_vector.reserve(res_left_vector->size() + res_right_vector->size()); - combined_vector.insert(combined_vector.end(), res_left_vector->begin(), res_left_vector->end()); - combined_vector.insert(combined_vector.end(), res_right_vector->begin(), res_right_vector->end()); - return std::make_shared>>(combined_vector); - } - - std::any visitVariableExpr(std::shared_ptr expr) override { - // Check if this variable points to a function - if (interpreter->getEnvironment()->get(expr->name) != nullptr) { - return std::any_cast>>>(interpreter->getEnvironment()->get(expr->name)); - } - return std::make_shared>>(); - } - - std::any visitCallExpr(std::shared_ptr expr) override { - // Get the function - auto res_function = expr->callee->accept(*this); - // If it's a variable, then it would return - - std::shared_ptr>> res_function_vector = std::any_cast>>>(res_function); - // Find the function name - - // Crucial: How to recursively descent into the call function and arguments, and find the subfunctions being called? - // Depending on the type of function, we descent differently - // Primitive function: we can have a default map - // Lambda function: we descent into each argument and expr - // Object function: we descent into each argument and expr - - // Get the arguments - std::vector> arguments; - for (auto &argument : expr->arguments) { - auto res_argument = argument->accept(*this); - std::shared_ptr>> res_argument_vector = std::any_cast>>>(res_argument); - arguments.insert(arguments.end(), res_argument_vector->begin(), res_argument_vector->end()); - } - // Call the function - for (auto &callable : *res_function_vector) { - callable->call(arguments); - } - return std::make_shared>>(); - } - - }; -} \ No newline at end of file diff --git a/install_scripts/setup_emscripten.sh b/install_scripts/setup_emscripten.sh old mode 100644 new mode 100755 index e28b602..c103b48 --- a/install_scripts/setup_emscripten.sh +++ b/install_scripts/setup_emscripten.sh @@ -1,10 +1,31 @@ -# Clone the Emscripten SDK repository -git clone https://github.com/emscripten-core/emsdk.git -cd emsdk +#!/usr/bin/env bash +# Setup Emscripten SDK for building Autumn WASM artifacts -# Fetch the latest version of the emsdk (not needed the first time you clone) -git pull +set -e -# Install and activate the latest SDK tools -./emsdk install latest -./emsdk activate latest +EMSDK_DIR="emsdk" + +# Check if emsdk is already installed +if [ -d "$EMSDK_DIR" ]; then + echo "✓ Emscripten SDK directory already exists at $EMSDK_DIR" + echo " Updating to latest version..." + cd "$EMSDK_DIR" + git pull + ./emsdk install latest + ./emsdk activate latest + echo "✓ Emscripten SDK updated and activated" +else + echo "Installing Emscripten SDK..." + # Clone the Emscripten SDK repository + git clone https://github.com/emscripten-core/emsdk.git + cd "$EMSDK_DIR" + + # Install and activate the latest SDK tools + ./emsdk install latest + ./emsdk activate latest + echo "✓ Emscripten SDK installed and activated" +fi + +echo "" +echo "To use Emscripten in your current shell, run:" +echo " source $EMSDK_DIR/emsdk_env.sh" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ee862ac --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,26 @@ +[build-system] +requires = ["scikit-build-core", "pybind11"] +build-backend = "scikit_build_core.build" + +[project] +name = "mara-autumn-cpp" +version = "1.0.0" +description = "C++ implementation of the Autumn DSL interpreter, exposed as a Python extension via pybind11." +readme = "README.md" +license = { file = "LICENSE" } +requires-python = ">=3.12" + +[project.optional-dependencies] +viz = ["matplotlib", "numpy"] + +[tool.scikit-build] +build.targets = ["interpreter_module"] +install.components = ["python_extension"] + +[tool.cibuildwheel] +build = [ + "cp31[234]-manylinux_x86_64", + "cp31[234]-manylinux_aarch64", + "cp31[234]-macosx_x86_64", + "cp31[234]-macosx_arm64", +] diff --git a/python_pkg/__init__.py b/python_pkg/__init__.py new file mode 100644 index 0000000..2c0a269 --- /dev/null +++ b/python_pkg/__init__.py @@ -0,0 +1,7 @@ +"""Autumn interpreter Python bindings (mara-autumn-cpp wheel).""" + +from . import interpreter_module +from .autumnstdlib import autumnstdlib +from .interpreter_module import Interpreter + +__all__ = ["Interpreter", "autumnstdlib", "interpreter_module"] diff --git a/python_pkg/autumnstdlib/__init__.py b/python_pkg/autumnstdlib/__init__.py new file mode 100644 index 0000000..a836951 --- /dev/null +++ b/python_pkg/autumnstdlib/__init__.py @@ -0,0 +1,7 @@ +"""Autumn standard library — bundled stdlib.sexp loaded as a string.""" + +from importlib.resources import read_text + +autumnstdlib = read_text(__name__, "stdlib.sexp") + +__all__ = ["autumnstdlib"] diff --git a/python_test.py b/python_test.py deleted file mode 100644 index 7461caa..0000000 --- a/python_test.py +++ /dev/null @@ -1,181 +0,0 @@ -import arcade -import interpreter_module -import json -import sys - -# Constants for grid cell size and margin -CELL_SIZE = 50 # Pixels -MARGIN = 5 # Pixels -DEFAULT_COLOR = arcade.color.WHITE - -# Define the color mapping for different grid entities -COLOR_MAP = { - "gray": arcade.color.GRAY, - "gold": arcade.color.GOLD, - "green": arcade.color.GREEN, - "mediumpurple": arcade.color.MEDIUM_PURPLE, - "purple": arcade.color.PURPLE, - "white": arcade.color.WHITE, - "yellow": arcade.color.YELLOW, - "blue": arcade.color.BLUE, - "red": arcade.color.RED, - "orange": arcade.color.ORANGE, - "darkgreen": arcade.color.DARK_GREEN, - # Add more mappings as needed -} - -UPDATE_INTERVAL = 0.2 # seconds -RENDER_INTERVAL = 0.5 # seconds - - -class GridRenderer(arcade.Window): - def __init__(self, interpreter, grid_size): - super().__init__( - grid_size * (CELL_SIZE + MARGIN) + MARGIN, - grid_size * (CELL_SIZE + MARGIN) + MARGIN, - "Grid Game", - ) - self.interpreter = interpreter - self.grid = {} - self.grid_size = grid_size - self.sprite_list = arcade.SpriteList() - self.set_update_rate(1 / 60) # 60 FPS - - def setup(self): - self.update_timer = 0 - self.render_timer = 0 - self.process_initial_render() - - def process_initial_render(self): - render_output = self.interpreter.render_all() - self.grid, self.grid_size = parse_grid(render_output) - self.create_sprites() - - def create_sprites(self): - self.sprite_list = arcade.ShapeElementList() - for elem in self.grid: - for subelem in self.grid[elem]: - col_idx = subelem["position"]["x"] - row_idx = subelem["position"]["y"] - color_key = subelem["color"].lower() - color = COLOR_MAP.get(color_key, DEFAULT_COLOR) - - x = MARGIN + col_idx * (CELL_SIZE + MARGIN) + CELL_SIZE / 2 - y = ( - MARGIN - + (self.grid_size - row_idx - 1) * (CELL_SIZE + MARGIN) - + CELL_SIZE / 2 - ) - - # Create a colored square as a sprite - shape = arcade.create_rectangle_filled( - x, y, CELL_SIZE, CELL_SIZE, color - ) - self.sprite_list.append(shape) - - def on_draw(self): - arcade.start_render() - self.sprite_list.draw() - - def on_update(self, delta_time): - self.update_timer += delta_time - self.render_timer += delta_time - - if self.update_timer >= UPDATE_INTERVAL: - self.interpreter.step() - self.update_timer = 0 - - if self.render_timer >= RENDER_INTERVAL: - self.render_grid() - self.render_timer = 0 - - def render_grid(self): - render_output = self.interpreter.render_all() - self.grid, self.grid_size = parse_grid(render_output) - self.create_sprites() - - def on_mouse_press(self, x, y, button, modifiers): - col = (x - MARGIN) // (CELL_SIZE + MARGIN) - row = (self.grid_size * (CELL_SIZE + MARGIN) - y - MARGIN) // ( - CELL_SIZE + MARGIN - ) - if 0 <= col < self.grid_size and 0 <= row < self.grid_size: - print(f"Grid clicked at Row: {row}, Col: {col}") - self.interpreter.click(col, row) - self.render_grid() - else: - print("Click was outside the grid boundaries.") - - def on_key_press(self, symbol, modifiers): - if symbol == arcade.key.LEFT: - self.interpreter.left() - self.render_grid() - elif symbol == arcade.key.RIGHT: - self.interpreter.right() - self.render_grid() - elif symbol == arcade.key.UP: - self.interpreter.up() - self.render_grid() - elif symbol == arcade.key.DOWN: - self.interpreter.down() - self.render_grid() - elif symbol == arcade.key.Q: - print("Quitting the game.") - arcade.close_window() - sys.exit() - else: - print(f"Unhandled key: {arcade.key.symbol_string(symbol)}") - - -def parse_grid(render_output: str): - """ - Parses the JSON string output from render_all() into a grid and its size. - - Args: - render_output (str): The JSON string representation of the grid. - - Returns: - tuple: A tuple containing the grid dictionary and grid size. - """ - try: - elem_dict = json.loads(render_output) - except json.JSONDecodeError as e: - print(f"JSON Decode Error: {e}") - return {}, 0 - grid = elem_dict - grid_size = elem_dict.pop("GRID_SIZE", 0) - return grid, grid_size - - -def main(): - # Initialize the Interpreter - interpreter = interpreter_module.Interpreter() - - # Check for script file argument - if len(sys.argv) > 1: - script_path = sys.argv[1] - else: - script_path = "./tests/test2.sexp" - - # Read and run the script - try: - with open(script_path, "r") as f: - script = f.read() - run_result = interpreter.run_script(script) - print("Run Script Result:", run_result) - except FileNotFoundError: - print(f"Script file {script_path} not found.") - run_result = "" - - # Assume GRID_SIZE is provided after running the script - render_output = interpreter.render_all() - grid, grid_size = parse_grid(render_output) - - # Initialize and run the renderer - window = GridRenderer(interpreter, grid_size) - window.setup() - arcade.run() - - -if __name__ == "__main__": - main() diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 9cb579c..0000000 --- a/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -pybind11[global] -matplotlib diff --git a/src/interpreter/AutumnStdComponents.cpp b/src/interpreter/AutumnStdComponents.cpp index 684acc9..3e71fc2 100644 --- a/src/interpreter/AutumnStdComponents.cpp +++ b/src/interpreter/AutumnStdComponents.cpp @@ -1,5 +1,4 @@ #include "AutumnStdComponents.hpp" -#include "AstPrinter.hpp" #include "AutumnCallable.hpp" #include "AutumnCallableValue.hpp" #include "AutumnConstructor.hpp" @@ -17,7 +16,6 @@ #include namespace Autumn { -std::shared_ptr AutumnFuncType::instance = nullptr; // If class initializer is nullptr, then we take all values forwarded // Then use them as init // else if it is not null, it have to return a list of values @@ -59,41 +57,53 @@ std::shared_ptr makeObjectClass( fieldTypes.push_back(field.second); } + auto &interner = interpreter.getInterner(); + const Symbol originId = interner.intern("origin"); + const Symbol allCellsId = interner.intern("allCells"); + const Symbol isListId = interner.intern("isList"); + std::vector paramLists; + std::vector paramIds; std::vector> fieldsExprs; fieldsExprs.reserve(fieldNames.size() + 1); for (auto fieldName : fieldNames) { + Symbol fieldId = interner.intern(fieldName); paramLists.push_back(Token(TokenType::IDENTIFIER, fieldName, nullptr, 0)); + paramIds.push_back(fieldId); fieldsExprs.push_back(std::make_shared( - Token(TokenType::IDENTIFIER, fieldName, nullptr, 0))); + Token(TokenType::IDENTIFIER, fieldName, nullptr, 0), fieldId)); } std::vector paramListsWithPosition(paramLists); paramListsWithPosition.push_back( Token(TokenType::IDENTIFIER, "origin", nullptr, 0)); + std::vector paramIdsWithPosition(paramIds); + paramIdsWithPosition.push_back(originId); - AstPrinter printer; - SExpParser tmpParser = SExpParser(""); + SExpParser tmpParser = SExpParser("", interpreter.getInterner()); fieldsExprs.push_back(std::make_shared( - Token(TokenType::IDENTIFIER, "origin", nullptr, 0))); + Token(TokenType::IDENTIFIER, "origin", nullptr, 0), originId)); // Wrap cell expr in a list add try { auto assignExpr = std::make_shared( - Token(TokenType::IDENTIFIER, "allCells", nullptr, 0), cexpr); + Token(TokenType::IDENTIFIER, "allCells", nullptr, 0), allCellsId, + cexpr); auto ifListTypeExpr = std::make_shared( std::make_shared( - Token(TokenType::IDENTIFIER, "isList", nullptr, 0)), + Token(TokenType::IDENTIFIER, "isList", nullptr, 0), isListId), std::vector>({std::make_shared( - Token(TokenType::IDENTIFIER, "allCells", nullptr, 0))})); + Token(TokenType::IDENTIFIER, "allCells", nullptr, 0), + allCellsId)})); auto ifListExpr = std::make_shared( ifListTypeExpr, std::make_shared( - Token(TokenType::IDENTIFIER, "allCells", nullptr, 0)), + Token(TokenType::IDENTIFIER, "allCells", nullptr, 0), allCellsId), std::make_shared( std::vector>({std::make_shared( - Token(TokenType::IDENTIFIER, "allCells", nullptr, 0))}))); + Token(TokenType::IDENTIFIER, "allCells", nullptr, 0), + allCellsId)}))); auto cellExprBlock = std::make_shared( std::vector>({assignExpr, ifListExpr})); @@ -106,8 +116,8 @@ std::shared_ptr makeObjectClass( std::shared_ptr allFieldsExpr = std::make_shared(fieldsExprs); - std::shared_ptr pLambda = - std::make_shared(paramListsWithPosition, allFieldsExpr); + std::shared_ptr pLambda = std::make_shared( + paramListsWithPosition, paramIdsWithPosition, allFieldsExpr); std::vector fullFieldNames(fieldNames); fullFieldNames.push_back(std::string("origin")); @@ -132,7 +142,7 @@ std::shared_ptr makeObjectClass( std::shared_ptr renderExpr = std::dynamic_pointer_cast(tmpParser.parseExpr(renderLambda)); std::shared_ptr renderCallable = - std::make_shared(renderExpr, nullptr); + std::make_shared(*renderExpr, nullptr); if (renderCallable == nullptr) { throw std::runtime_error( "makeObjectClass: Failed to create callable for render method"); diff --git a/src/interpreter/Environment.cpp b/src/interpreter/Environment.cpp index f0ccba0..7b5090c 100644 --- a/src/interpreter/Environment.cpp +++ b/src/interpreter/Environment.cpp @@ -2,73 +2,68 @@ #include "Error.hpp" #include -Autumn::Environment::Environment() : enclosing(nullptr), environmentType(EnvironmentType::GLOBAL) {} - -Autumn::Environment::Environment(EnvironmentPtr enclosingEnv, EnvironmentType environmentType) - : enclosing(enclosingEnv), environmentType(environmentType) {} - -Autumn::EnvironmentPtr Autumn::Environment::ancestor(int distance) { - EnvironmentPtr environment = shared_from_this(); +Autumn::Environment::Environment(Interner &interner) + : environmentType(EnvironmentType::GLOBAL), interner_(&interner), + enclosing(nullptr) {} + +Autumn::Environment::Environment(EnvironmentPtr enclosingEnv, + EnvironmentType environmentType) + : environmentType(environmentType), + interner_(enclosingEnv ? enclosingEnv->interner_ : nullptr), + enclosing(enclosingEnv) {} + +Autumn::Environment *Autumn::Environment::ancestor(int distance) { + Environment *env = this; for (int i = 0; i < distance; i++) { - environment = environment->enclosing; + env = env->enclosing.get(); } - return environment; + return env; } -void Autumn::Environment::define(std::string name, +void Autumn::Environment::define(Symbol name, std::shared_ptr value) { values[name] = value; definitionOrder.push_back(name); updateStates[name] = false; } -void Autumn::Environment::defineType(std::string name, +void Autumn::Environment::defineType(Symbol name, std::shared_ptr value) { typeValues[name] = value; } std::shared_ptr Autumn::Environment::getAt(int distance, const std::string &name) { - return ancestor(distance)->values[name]; + return ancestor(distance)->values[interner_->intern(name)]; } void Autumn::Environment::assignAt( int distance, const Token &name, const std::shared_ptr &value) { - ancestor(distance)->values[name.lexeme] = value; - updateStates[name.lexeme] = true; -} - -std::shared_ptr -Autumn::Environment::get(const Token &name) { - if (values.find(name.lexeme) != values.end()) { - return values[name.lexeme]; - } - - if (enclosing != nullptr) { - return enclosing->get(name); - } - - throw Error(std::string("Undefined variable '" + name.lexeme + "'. Defined arguments are: ") + this->printAllDefinedVariablesCrossStack()); + Symbol sym = interner_->intern(name.lexeme); + ancestor(distance)->values[sym] = value; + updateStates[sym] = true; } std::shared_ptr -Autumn::Environment::get(const std::string &name) { - if (values.find(name) != values.end()) { - return values[name]; +Autumn::Environment::get(Symbol name) { + if (auto it = values.find(name); it != values.end()) { + return it->second; } if (enclosing != nullptr) { return enclosing->get(name); } - throw Error(std::string("Undefined variable '" + name + "'. Defined arguments are: ") + this->printAllDefinedVariablesCrossStack()); + throw Error(std::string("Undefined variable '" + *name + + "'. Defined arguments are: ") + + this->printAllDefinedVariablesCrossStack()); } std::shared_ptr -Autumn::Environment::getTypeValue(const Token &name) { - if (typeValues.find(name.lexeme) != typeValues.end()) { - return typeValues[name.lexeme]; +Autumn::Environment::getTypeValue(Symbol name) { + if (auto it = typeValues.find(name); it != typeValues.end()) { + return it->second; } if (enclosing != nullptr) { @@ -78,25 +73,20 @@ Autumn::Environment::getTypeValue(const Token &name) { return nullptr; } -void Autumn::Environment::assign(const Token &name, - const std::shared_ptr &value) { - return assign(name.lexeme, value); -} - -void Autumn::Environment::assign(const std::string &name, +void Autumn::Environment::assign(Symbol name, const std::shared_ptr &value) { - if (values.find(name) != values.end()) { - auto oldType = values[name]->getType(); + if (auto it = values.find(name); it != values.end()) { + auto oldType = it->second->getType(); auto newType = value->getType(); if (oldType->toString() != newType->toString() && std::dynamic_pointer_cast(value) == nullptr) { throw Error(std::string("Cannot assign value of type '") + newType->toString() + "' to variable of type '" + - oldType->toString() + "' for variable '" + name + "'."); + oldType->toString() + "' for variable '" + *name + "'."); } - int oldInstId = values[name]->getInstId(); - values[name] = value; - values[name]->setInstId(oldInstId); + int oldInstId = it->second->getInstId(); + it->second = value; + it->second->setInstId(oldInstId); updateStates[name] = true; return; } else { @@ -112,7 +102,7 @@ void Autumn::Environment::assign(const std::string &name, } } -void Autumn::Environment::assignType(std::string name, +void Autumn::Environment::assignType(Symbol name, const std::shared_ptr &type) { assignedTypes[name] = type; } diff --git a/src/interpreter/ExprMethods.cpp b/src/interpreter/ExprMethods.cpp new file mode 100644 index 0000000..0711e59 --- /dev/null +++ b/src/interpreter/ExprMethods.cpp @@ -0,0 +1,219 @@ +#include "Expr.hpp" +#include "Stmt.hpp" +#include +#include + +namespace Autumn { + +// --- prettyPrint implementations --- + +std::string Assign::prettyPrint() { + return "( " + name.lexeme + " " + value->prettyPrint() + ")"; +} + +std::string Binary::prettyPrint() { + return "(" + op.lexeme + " " + left->prettyPrint() + " " + right->prettyPrint() + ")"; +} + +std::string Call::prettyPrint() { + std::string result = "( " + callee->prettyPrint(); + for (const auto &arg : arguments) { + result += " " + arg->prettyPrint(); + } + return result + ")"; +} + +std::string Get::prettyPrint() { + return "(" + name.lexeme + " " + object->prettyPrint() + ")"; +} + +std::string Grouping::prettyPrint() { + return "(group " + expression->prettyPrint() + ")"; +} + +std::string IntLiteral::prettyPrint() { + return "(NUMBER " + std::to_string(value) + ")"; +} + +std::string BoolLiteral::prettyPrint() { + return value ? std::string("(BOOL true)") : std::string("(BOOL false)"); +} + +std::string StringLiteral::prettyPrint() { + return "(STRING " + value + ")"; +} + +std::string Logical::prettyPrint() { + return "(" + op.lexeme + " " + left->prettyPrint() + " " + right->prettyPrint() + ")"; +} + +std::string Set::prettyPrint() { + return "(" + name.lexeme + " " + object->prettyPrint() + " " + value->prettyPrint() + ")"; +} + +std::string Unary::prettyPrint() { + return "(" + op.lexeme + " " + right->prettyPrint() + ")"; +} + +std::string Lambda::prettyPrint() { + std::string paramsStr = "("; + for (const auto &p : params) paramsStr += p.lexeme + " "; + paramsStr += ")"; + return "(lambda " + paramsStr + " " + right->prettyPrint() + ")"; +} + +std::string Variable::prettyPrint() { + return "(var " + name.lexeme + ")"; +} + +std::string TypeVariable::prettyPrint() { + return "( " + name.lexeme + ")"; +} + +std::string TypeDecl::prettyPrint() { + return "( " + name.lexeme + " " + typeexpr->prettyPrint() + ")"; +} + +std::string ListTypeExpr::prettyPrint() { + return "( " + typeexpr->prettyPrint() + ")"; +} + +std::string ListVarExpr::prettyPrint() { + std::string result = "["; + for (const auto &v : varExprs) result += v->prettyPrint(); + return result + "]"; +} + +std::string IfExpr::prettyPrint() { + return "(if " + condition->prettyPrint() + " " + + thenBranch->prettyPrint() + " " + elseBranch->prettyPrint() + ")"; +} + +std::string Let::prettyPrint() { + std::string result = "{"; + for (const auto &e : exprs) result += e->prettyPrint(); + return result + "}"; +} + +std::string InitNext::prettyPrint() { + return "(initnext " + initializer->prettyPrint() + " " + nextExpr->prettyPrint() + ")"; +} + +// --- collectVars implementations --- + +std::vector Assign::collectVars() { + return value->collectVars(); +} + +std::vector Binary::collectVars() { + auto l = left->collectVars(); + auto r = right->collectVars(); + l.insert(l.end(), r.begin(), r.end()); + return l; +} + +std::vector Call::collectVars() { + auto result = callee->collectVars(); + for (const auto &arg : arguments) { + auto v = arg->collectVars(); + result.insert(result.end(), v.begin(), v.end()); + } + return result; +} + +std::vector Get::collectVars() { + auto result = object->collectVars(); + result.push_back(name.lexeme); + return result; +} + +std::vector Grouping::collectVars() { + return expression->collectVars(); +} + +std::vector Logical::collectVars() { + auto l = left->collectVars(); + auto r = right->collectVars(); + l.insert(l.end(), r.begin(), r.end()); + return l; +} + +std::vector Set::collectVars() { + return value->collectVars(); +} + +std::vector Unary::collectVars() { + return right->collectVars(); +} + +std::vector Lambda::collectVars() { + return right->collectVars(); +} + +std::vector Variable::collectVars() { + return {name.lexeme}; +} + +std::vector ListVarExpr::collectVars() { + std::vector result; + for (const auto &v : varExprs) { + auto sub = v->collectVars(); + result.insert(result.end(), sub.begin(), sub.end()); + } + return result; +} + +std::vector IfExpr::collectVars() { + auto c = condition->collectVars(); + auto t = thenBranch->collectVars(); + auto e = elseBranch->collectVars(); + c.insert(c.end(), t.begin(), t.end()); + c.insert(c.end(), e.begin(), e.end()); + return c; +} + +std::vector Let::collectVars() { + std::vector result; + for (const auto &e : exprs) { + auto sub = e->collectVars(); + result.insert(result.end(), sub.begin(), sub.end()); + } + return result; +} + +std::vector InitNext::collectVars() { + auto i = initializer->collectVars(); + auto n = nextExpr->collectVars(); + i.insert(i.end(), n.begin(), n.end()); + return i; +} + +// --- Stmt prettyPrint implementations --- + +std::string Block::prettyPrint() { + std::string result; + for (const auto &stmt : statements) { + result += stmt->prettyPrint(); + } + return "{" + result + "}"; +} + +std::string Object::prettyPrint() { + std::string fieldsStr = ":("; + for (const auto &field : fields) { + fieldsStr += field->prettyPrint(); + } + fieldsStr += ")"; + std::string cellStr = Cell->prettyPrint(); + return "( " + name.lexeme + " " + fieldsStr + " " + cellStr + ")"; +} + +std::string Expression::prettyPrint() { + return expression->prettyPrint(); +} + +std::string OnStmt::prettyPrint() { + return "(on " + condition->prettyPrint() + ": " + expr->prettyPrint() + ")"; +} + +} // namespace Autumn diff --git a/src/interpreter/Interpreter.cpp b/src/interpreter/Interpreter.cpp index 763b9fb..d007c6b 100644 --- a/src/interpreter/Interpreter.cpp +++ b/src/interpreter/Interpreter.cpp @@ -1,6 +1,4 @@ -#include "VarCollector.hpp" #include "Interpreter.hpp" -#include "AstPrinter.hpp" #include "AutumnCallableValue.hpp" #include "AutumnExprValue.hpp" #include "AutumnInstance.hpp" @@ -32,7 +30,8 @@ Interpreter::Interpreter() { } void Interpreter::init(std::string stdlib) { - globals = std::make_shared(); + auto &interp = *this; + globals = std::make_shared(interner_); environment = globals; // Reset onClauseCovered onClauseCovered.clear(); @@ -133,346 +132,116 @@ void Interpreter::init(std::string stdlib) { // read AutumnStdLib if (stdlib == "") { std::string autumnStdLib = readFile("autumnstdlib/stdlib.sexp"); - SExpParser parser(autumnStdLib); + SExpParser parser(autumnStdLib, interner_); std::vector> stmts = parser.parseStmt(); for (const auto &stmt : stmts) { - stmt->accept(*this); + stmt->exec(interp); } } else { - SExpParser parser(stdlib); + SExpParser parser(stdlib, interner_); std::vector> stmts = parser.parseStmt(); for (const auto &stmt : stmts) { - stmt->accept(*this); + stmt->exec(interp); } } } -// If need to be evaluated, evaluate as normal -std::any Interpreter::visitLiteralExpr(std::shared_ptr expr) { - std::any value = expr->value; - // Check if value is a number, a boolean, or a string - try { - return std::shared_ptr( - std::make_shared(std::any_cast(value))); - } catch (const std::bad_any_cast &) { - try { - return std::shared_ptr( - std::make_shared(std::any_cast(value))); - } catch (const std::bad_any_cast &) { - try { - return std::shared_ptr( - std::make_shared(std::any_cast(value))); - } catch (const std::bad_any_cast &) { - throw Error("Unknown type of literal"); - } - } - } +std::shared_ptr IntLiteral::eval(Interpreter &interp) { + return std::shared_ptr( + std::make_shared(value)); } -std::any Interpreter::visitGroupingExpr(std::shared_ptr expr) { - throw Error("This language does not supposed to have grouping :)"); +std::shared_ptr BoolLiteral::eval(Interpreter &interp) { + return std::shared_ptr( + std::make_shared(value)); } -std::any Interpreter::visitUnaryExpr(std::shared_ptr expr) { - std::shared_ptr right; - try { - right = - std::any_cast>(expr->right->accept(*this)); - } catch (const std::bad_any_cast &) { - throw Error("Unary operator must be applied to a value"); - } - switch (expr->op.type) { - case TokenType::MINUS: { - std::shared_ptr rightNumber = - std::dynamic_pointer_cast(right); - if (rightNumber == nullptr) { - throw Error("Unary - must be applied to a number"); - } - // std::cerr << "Unary -" << rightNumber->getNumber() << std::endl; - return std::shared_ptr( - std::make_shared(-rightNumber->getNumber())); - } - case TokenType::PLUS: { - std::shared_ptr rightNumber = - std::dynamic_pointer_cast(right); - if (rightNumber == nullptr) { - throw Error("Unary + must be applied to a number"); - } - // std::cerr << "Unary +" << rightNumber->getNumber() << std::endl; - return std::shared_ptr( - std::make_shared(rightNumber->getNumber())); - } - case TokenType::BANG: { - std::shared_ptr rightBool = - std::dynamic_pointer_cast(right); - if (rightBool == nullptr) { - throw Error("Unary ! must be applied to a boolean"); - } - auto res = std::make_shared(!rightBool->getBool()); - // std::cerr << "Unary !" << rightBool->getBool() << " = " << res->getBool() - // << std::endl; - return std::dynamic_pointer_cast(res); - } - default: - throw Error("Unknown unary operator"); - } +std::shared_ptr StringLiteral::eval(Interpreter &interp) { + return std::shared_ptr( + std::make_shared(value)); } -std::pair, std::shared_ptr> -getBinaryNumber(std::shared_ptr left, - std::shared_ptr right) { - try { - std::shared_ptr leftNumber = - std::dynamic_pointer_cast(left); - std::shared_ptr rightNumber = - std::dynamic_pointer_cast(right); - if (leftNumber == nullptr || rightNumber == nullptr) { - throw Error("Binary operator must be applied to two numbers, instead got " + - left->toString() + " and " + right->toString()); - } - return std::make_pair(leftNumber, rightNumber); - } catch (const std::bad_any_cast &) { - throw Error("Binary operator must be applied to two numbers, instead got " + - left->toString() + " and " + right->toString()); - } +std::shared_ptr Grouping::eval(Interpreter &interp) { + throw Error("This language does not supposed to have grouping :)"); } -std::any Interpreter::visitBinaryExpr(std::shared_ptr expr) { - std::shared_ptr left, right; - try { - left = - std::any_cast>(expr->left->accept(*this)); - right = - std::any_cast>(expr->right->accept(*this)); - } catch (const std::bad_any_cast &e) { - AstPrinter printer; - throw Error("Binary operator must be applied to a value, instead got " + - printer.print(expr->left) + " and " + - printer.print(expr->right)); - } - std::shared_ptr res; - switch (expr->op.type) { - case TokenType::PLUS: { - try{ - std::pair, std::shared_ptr> - numbers = getBinaryNumber(left, right); - res = std::make_shared(numbers.first->getNumber() + - numbers.second->getNumber()); - } catch (const Error &e) { - throw Error("Binary + must be applied to two numbers, instead got " + - left->toString() + " and " + right->toString() + " op: " + expr->op.lexeme + " - " + e.what()); - } - break; - } - case TokenType::MINUS: { - try{ - std::pair, std::shared_ptr> - numbers = getBinaryNumber(left, right); - res = std::make_shared(numbers.first->getNumber() - - numbers.second->getNumber()); - } catch (const Error &e) { - throw Error("Binary - must be applied to two numbers, instead got " + - left->toString() + " and " + right->toString() + " op: " + expr->op.lexeme + " - " + e.what()); - } - break; - } - case TokenType::STAR: { - try{ - std::pair, std::shared_ptr> - numbers = getBinaryNumber(left, right); - res = std::make_shared(numbers.first->getNumber() * - numbers.second->getNumber()); - } catch (const Error &e) { - throw Error("Binary * must be applied to two numbers, instead got " + - left->toString() + " and " + right->toString() + " op: " + expr->op.lexeme + " - " + e.what()); - } - break; - } - case TokenType::SLASH: { - try{ - std::pair, std::shared_ptr> - numbers = getBinaryNumber(left, right); - res = std::make_shared(numbers.first->getNumber() / - numbers.second->getNumber()); - } catch (const Error &e) { - throw Error("Binary / must be applied to two numbers, instead got " + - left->toString() + " and " + right->toString() + " op: " + expr->op.lexeme + " - " + e.what()); - } - break; - } - case TokenType::MODULO: { - try{ - std::pair, std::shared_ptr> - numbers = getBinaryNumber(left, right); - res = std::make_shared(numbers.first->getNumber() % - numbers.second->getNumber()); - } catch (const Error &e) { - throw Error("Binary % must be applied to two numbers, instead got " + - left->toString() + " and " + right->toString() + " op: " + expr->op.lexeme + " - " + e.what()); - } - break; - } - case TokenType::GREATER: { - try{ - std::pair, std::shared_ptr> - numbers = getBinaryNumber(left, right); - res = std::make_shared(numbers.first->getNumber() > - numbers.second->getNumber()); - } catch (const Error &e) { - throw Error("Binary > must be applied to two numbers, instead got " + - left->toString() + " and " + right->toString() + " op: " + expr->op.lexeme + " - " + e.what()); - } - break; - } - case TokenType::GREATER_EQUAL: { - try{ - std::pair, std::shared_ptr> - numbers = getBinaryNumber(left, right); - res = std::make_shared(numbers.first->getNumber() >= - numbers.second->getNumber()); - } catch (const Error &e) { - throw Error("Binary >= must be applied to two numbers, instead got " + - left->toString() + " and " + right->toString() + " op: " + expr->op.lexeme + " - " + e.what()); - } - break; - } - case TokenType::LESS: { - try{ - std::pair, std::shared_ptr> - numbers = getBinaryNumber(left, right); - res = std::make_shared(numbers.first->getNumber() < - numbers.second->getNumber()); - } catch (const Error &e) { - throw Error("Binary < must be applied to two numbers, instead got " + - left->toString() + " and " + right->toString() + " op: " + expr->op.lexeme + " - " + e.what()); - } - break; - } - case TokenType::LESS_EQUAL: { - try { - std::pair, std::shared_ptr> - numbers = getBinaryNumber(left, right); - res = std::make_shared(numbers.first->getNumber() <= - numbers.second->getNumber()); - } catch (const Error &e) { - throw Error("Binary <= must be applied to two numbers, instead got " + - left->toString() + " and " + right->toString() + " op: " + expr->op.lexeme + " - " + e.what()); - } - break; - } - case TokenType::EQUAL_EQUAL: { - try { - // std::cout << "Comparing " << left->toString() << " and " - // << right->toString() << std::endl; - res = std::dynamic_pointer_cast( - std::make_shared(left->isEqual(right))); - return res; - // std::cout << "Result: " << res->toString() << std::endl; - break; - } catch (const std::bad_any_cast &e) { - throw Error("Cannot compare different types"); - } - } - case TokenType::BANG_EQUAL: { - try { - res = std::dynamic_pointer_cast( - std::make_shared(!left->isEqual(right))); - return res; - } catch (const std::bad_any_cast &e) { - throw Error("Cannot compare different types"); - } - } - default: - throw Error("Unknown binary operator"); - } - // std::cout << "Returning from binary: " << res->toString() << std::endl; - return std::dynamic_pointer_cast(res); +std::shared_ptr Unary::eval(Interpreter &interp) { + return right->eval(interp)->eval_unary(op); } -std::any Interpreter::visitVariableExpr(std::shared_ptr expr) { - // std::cerr << "Visiting variable " << expr->name.lexeme << std::endl; - try { - std::shared_ptr tv = std::any_cast>( - environment->getTypeValue(expr->name)); - if (tv != nullptr) { - // std::cerr << "Getting type value " << tv->toString() << std::endl; - return tv; - } - } catch (const std::bad_any_cast &) { - std::cerr << "Failing to get type having name " - << environment->printAllTypeValues() << " - Must be a variable" - << std::endl; - } - try { - return environment->get(expr->name); - } catch (const RuntimeError &e) { - std::cerr << "Undefined variable '" + expr->name.lexeme + "'" + - environment->printAllDefinedVariables() + "\n" + - "Current expr: " + AstPrinter().print(expr); - throw Error("Undefined variable '" + expr->name.lexeme + "'" + - environment->printAllDefinedVariablesCrossStack() + "\n" + - "Current expr: " + AstPrinter().print(expr)); +std::shared_ptr Binary::eval(Interpreter &interp) { + auto leftVal = left->eval(interp); + auto rightVal = right->eval(interp); + return leftVal->binop_on(*rightVal, op); +} + +std::shared_ptr Variable::eval(Interpreter &interp) { + // Check type environment for class constructors + auto tv = interp.environment->getTypeValue(nameId); + if (tv != nullptr) { + auto cls = std::dynamic_pointer_cast(tv); + if (cls) return std::make_shared(cls); + // Non-class type (e.g., primitive type annotation) — fall through to value lookup } + return interp.environment->get(nameId); } -std::any Interpreter::visitAssignExpr(std::shared_ptr expr) { - // std::cerr << "Assigning " << expr->name.lexeme << std::endl; +std::shared_ptr Assign::eval(Interpreter &interp) { + // std::cerr << "Assigning " << name.lexeme << std::endl; std::shared_ptr initNext = - std::dynamic_pointer_cast(expr->value); + std::dynamic_pointer_cast(value); if (initNext != nullptr) { - initOrder.push_back(expr->name.lexeme); - initMap[expr->name.lexeme] = initNext->initializer; - nextMap[expr->name.lexeme] = initNext->nextExpr; + interp.initNextVars.push_back( + {nameId, initNext->initializer, initNext->nextExpr}); return nullptr; } - std::any retVal = expr->value->accept(*this); + std::shared_ptr retVal = value->eval(interp); try { std::shared_ptr value = - std::any_cast>(retVal); - auto isNameInGlobal = globals->isDefined(expr->name.lexeme); + retVal; + auto isNameInGlobal = interp.globals->isDefined(nameId); // Get type value std::shared_ptr tv = - environment->getAssignedType(expr->name.lexeme); + interp.environment->getAssignedType(nameId); if (tv != nullptr && std::dynamic_pointer_cast(tv) == nullptr) { if (tv->toString() != value->getType()->toString()) { throw Error("Cannot assign value of type '" + value->getType()->toString() + "' to variable of type '" + - tv->toString() + "' for variable '" + expr->name.lexeme + + tv->toString() + "' for variable '" + name.lexeme + "'."); } } - environment->assign(expr->name, value); - if (environment != globals && !isNameInGlobal) { - if (globals->isDefined(expr->name.lexeme)) { + interp.environment->assign(nameId, value); + if (interp.environment != interp.globals && !isNameInGlobal) { + if (interp.globals->isDefined(nameId)) { throw Error("Variable assigned at local scope became defined at global " "scope: " + - expr->name.lexeme); //+ - // "\n Global: " + globals->printAllDefinedVariables() + - // "\n Local: " + environment->printAllDefinedVariables()); + name.lexeme); //+ + // "\n Global: " + interp.globals->printAllDefinedVariables() + + // "\n Local: " + interp.environment->printAllDefinedVariables()); } } - // std::cerr << "Assigned " << expr->name.lexeme << " to " << + // std::cerr << "Assigned " << name.lexeme << " to " << // value->toString() // << std::endl; return value; } catch (const std::bad_any_cast &e) { throw Error("Assign only accept either value or InitNext " + - AstPrinter().print(expr->value)); + value->prettyPrint()); } } -std::any Interpreter::visitLogicalExpr(std::shared_ptr expr) { - std::shared_ptr left = - std::any_cast>(expr->left->accept(*this)); - auto callableLeft = std::dynamic_pointer_cast(left); +std::shared_ptr Logical::eval(Interpreter &interp) { + auto left_val = left->eval(interp); + auto callableLeft = std::dynamic_pointer_cast(left_val); if (callableLeft != nullptr) { - left = callableLeft->call(*this, {}); + left_val = callableLeft->call(interp, {}); } std::shared_ptr ret = nullptr; - if (expr->op.type == TokenType::OR) { - if (isTruthy(left)) { + if (op.type == TokenType::OR) { + if (left_val->isTruthy()) { ret = std::dynamic_pointer_cast( std::make_shared(true)); // std::cerr << "Returning from logical: " << ret->toString() << @@ -480,72 +249,60 @@ std::any Interpreter::visitLogicalExpr(std::shared_ptr expr) { return ret; } } else { - if (!isTruthy(left)) { + if (!left_val->isTruthy()) { ret = std::dynamic_pointer_cast( std::make_shared(false)); - // std::cerr << "Returning from logical: " << ret->toString() << - // std::endl; return ret; } } try { - auto rightValue = - std::any_cast>(expr->right->accept(*this)); + auto right_val = + right->eval(interp); auto callableRight = - std::dynamic_pointer_cast(rightValue); + std::dynamic_pointer_cast(right_val); if (callableRight != nullptr) { - rightValue = callableRight->call(*this, {}); + right_val = callableRight->call(interp, {}); } return std::dynamic_pointer_cast( - std::make_shared(rightValue->isTruthy())); + std::make_shared(right_val->isTruthy())); } catch (const std::bad_any_cast &e) { // std::cerr << "Logical operator must be applied to a value" << std::endl; throw Error("Logical operator must be applied to a value" + - AstPrinter().print(expr->right)); + right->prettyPrint()); } } static std::shared_ptr>> -getAllArgs(std::shared_ptr expr, Interpreter &interpreter) { - std::vector> arguments; - int i = 0; - for (const auto &argument : expr->arguments) { +getAllArgs(Call &expr, Interpreter &interpreter) { + auto vals = std::make_shared>>(); + vals->reserve(expr.arguments.size()); + for (const auto &argument : expr.arguments) { try { - arguments.push_back(std::any_cast>( - argument->accept(interpreter))); - } catch (const std::bad_any_cast &e) { - throw Error("Call arguments must be values"); + vals->push_back(argument->eval(interpreter)); } catch (const Error &e) { throw Error("Call argument processing error while visiting: \n" + - AstPrinter().print(expr) + "\n Got: \n" + e.what()); + expr.prettyPrint() + "\n Got: \n" + e.what()); } } - return std::make_shared>>(arguments); + return vals; } -std::any Interpreter::visitCallExpr(std::shared_ptr expr) { - AstPrinter printer; - std::any callee = expr->callee->accept(*this); +std::shared_ptr Call::eval(Interpreter &interp) { + std::shared_ptr calleeVal = + callee->eval(interp); - try { - std::shared_ptr tv = - std::any_cast>(callee); - if (tv != nullptr) { - // Check if this is a class - std::shared_ptr cls = - std::dynamic_pointer_cast(tv); - if (cls != nullptr) { + auto classVal = std::dynamic_pointer_cast(calleeVal); + if (classVal != nullptr) { + std::shared_ptr cls = classVal->getClass(); + { if (cls->name == "Cell") { - std::shared_ptr> varExprs = std::make_shared>(); - VariableCollector collector; - for (const auto &arg : expr->arguments) { - auto subVarExprs = std::any_cast>>(arg->accept(collector)); - varExprs->insert(varExprs->end(), subVarExprs->begin(), subVarExprs->end()); - } - for (const auto &arg : expr->arguments) { - auto subVarExprs = std::any_cast>>(arg->accept(collector)); - varExprs->insert(varExprs->end(), subVarExprs->begin(), subVarExprs->end()); + std::vector varExprs; + for (const auto &arg : arguments) { + auto subVarExprs = arg->collectVars(); + for (const auto &v : subVarExprs) { + varExprs.push_back(interp.interner_.intern(v)); + } } // std::cout << "VarExprs: " << varExprs->size() << std::endl; // for (const auto &varExpr : *varExprs) { @@ -553,11 +310,11 @@ std::any Interpreter::visitCallExpr(std::shared_ptr expr) { // } std::vector> cellArgs = {}; // Create a closure - EnvironmentPtr subEnv = std::make_shared(); + EnvironmentPtr subEnv = std::make_shared(interp.interner_); // Copy everthing we can except the nearest instance enclosing. EnvironmentPtr skippingEnv = nullptr; // Find the nearest instance enclosing - EnvironmentPtr currentEnv = environment; + EnvironmentPtr currentEnv = interp.environment; std::stack envStack; while (currentEnv != nullptr) { @@ -574,24 +331,24 @@ std::any Interpreter::visitCallExpr(std::shared_ptr expr) { } else { // Copy everything over - subEnv->selectiveCopy(currentEnv, *varExprs); + subEnv->selectiveCopy(currentEnv, varExprs); } } - EnvironmentPtr currEnv = this->getEnvironment(); - this->setEnvironment(subEnv); + EnvironmentPtr currEnv = interp.getEnvironment(); + interp.setEnvironment(subEnv); // TODO: Separate out/ remove outer attribute that are not evaluated - for (const auto &arg : expr->arguments) { + for (const auto &arg : arguments) { std::shared_ptr exprValue = std::make_shared(arg, subEnv); try { - + std::shared_ptr pValue = - std::any_cast>( - arg->accept(*this)); + + arg->eval(interp); auto pNumber = std::dynamic_pointer_cast(pValue); if (pNumber != nullptr) { cellArgs.push_back(std::make_shared( - std::make_shared(pNumber->getNumber()), subEnv)); + std::make_shared(pNumber->getNumber()), subEnv)); continue; } cellArgs.push_back(exprValue); @@ -599,17 +356,17 @@ std::any Interpreter::visitCallExpr(std::shared_ptr expr) { cellArgs.push_back(exprValue); } } - this->setEnvironment(currEnv); + interp.setEnvironment(currEnv); return std::dynamic_pointer_cast( std::make_shared(cls, cellArgs)); } // First get all the argument - auto arguments = getAllArgs(expr, *this); + auto arguments = getAllArgs(*this, interp); try { std::vector> args = *arguments; if (cls->getInitializer() != nullptr) { std::shared_ptr pValue = - cls->getInitializer()->call(*this, *arguments); + cls->getInitializer()->call(interp, *arguments); std::shared_ptr list = std::dynamic_pointer_cast(pValue); if (list != nullptr) { @@ -627,37 +384,27 @@ std::any Interpreter::visitCallExpr(std::shared_ptr expr) { e.what()); } } - } - } catch (const std::bad_any_cast &) { - } - std::shared_ptr calleeVal = nullptr; - try { - calleeVal = std::any_cast>(callee); - calleeVal = calleeVal->copy(); - } catch (const std::bad_any_cast &e) { - throw Error( - "Callee:: Can only call functions and classes or evaluate expresions"); } try { std::shared_ptr exprValue = std::dynamic_pointer_cast(calleeVal); if (exprValue != nullptr) { - // Print out this and the enclosing environment environment type - std::string thisEnvType = this->getEnvironment()->getEnvironmentType() == EnvironmentType::INSTANCE ? "Instance" : "Global"; - std::string enclosingEnvType = this->getEnvironment()->getEnclosing() == nullptr ? "Global" : this->getEnvironment()->getEnclosing()->getEnvironmentType() == EnvironmentType::INSTANCE ? "Instance" : "Global"; + // Print out this and the enclosing interp.environment interp.environment type + std::string thisEnvType = interp.getEnvironment()->getEnvironmentType() == EnvironmentType::INSTANCE ? "Instance" : "Global"; + std::string enclosingEnvType = interp.getEnvironment()->getEnclosing() == nullptr ? "Global" : interp.getEnvironment()->getEnclosing()->getEnvironmentType() == EnvironmentType::INSTANCE ? "Instance" : "Global"; EnvironmentPtr subEnv = exprValue->getCenv(); - EnvironmentPtr currEnv = this->getEnvironment(); + EnvironmentPtr currEnv = interp.getEnvironment(); subEnv->setEnclosing(currEnv); - this->setEnvironment(subEnv); - auto val = exprValue->getExpr()->accept(*this); + interp.setEnvironment(subEnv); + auto val = exprValue->getExpr()->eval(interp); subEnv->setEnclosing(nullptr); - this->setEnvironment(currEnv); + interp.setEnvironment(currEnv); return val; } } catch (Error &e) { std::shared_ptr exprValue = std::dynamic_pointer_cast(calleeVal); - throw Error(std::string("Error in visiting evaluation of expression: ") + AstPrinter().print(exprValue->getExpr()) + "\n" + + throw Error(std::string("Error in visiting evaluation of expression: ") + exprValue->getExpr()->prettyPrint() + "\n" + e.what()); } std::shared_ptr callable = @@ -667,79 +414,79 @@ std::any Interpreter::visitCallExpr(std::shared_ptr expr) { } if (callable->toString() == "" || callable->toString() == ">") { // Do not evaluate the arguments - if (expr->arguments.size() != 1) { + if (arguments.size() != 1) { throw Error("[Prev] Prev() takes 1 argument"); } - std::shared_ptr varExpr = std::dynamic_pointer_cast(expr->arguments[0]); + std::shared_ptr varExpr = std::dynamic_pointer_cast(arguments[0]); if (varExpr == nullptr) { // This is already a string std::shared_ptr retVal = - std::any_cast>( - callable->call(*this, *getAllArgs(expr, *this))); + + callable->call(interp, *getAllArgs(*this, interp)); return retVal; } std::shared_ptr varName = std::make_shared(varExpr->name.lexeme); std::shared_ptr retVal = - std::any_cast>( - callable->call(*this, {varName})); + + callable->call(interp, {varName}); return retVal; } else { std::shared_ptr retVal = - std::any_cast>( - callable->call(*this, *getAllArgs(expr, *this))); + + callable->call(interp, *getAllArgs(*this, interp)); return retVal; } } -std::any Interpreter::visitGetExpr(std::shared_ptr expr) { - // std::cerr << "Visiting get: " << expr->name.lexeme << std::endl; - std::shared_ptr object = - std::any_cast>(expr->object->accept(*this)); - if (object == nullptr) { - throw Error("Error interpreting: " + AstPrinter().print(expr) + +std::shared_ptr Get::eval(Interpreter &interp) { + // std::cerr << "Visiting get: " << name.lexeme << std::endl; + std::shared_ptr object_val = + object->eval(interp); + if (object_val == nullptr) { + throw Error("Error interpreting: " + prettyPrint() + ": Cannot get property from null"); } std::shared_ptr instance = - std::dynamic_pointer_cast(object); + std::dynamic_pointer_cast(object_val); if (instance == nullptr) { - throw Error("Error interpreting: " + AstPrinter().print(expr) + + throw Error("Error interpreting: " + prettyPrint() + "Only instances have properties, instead got " + - object->getType()->toString()); + object_val->getType()->toString()); } - std::shared_ptr value = instance->get(expr->name.lexeme); + std::shared_ptr value = + AutumnInstance::getWithMethod(instance, name.lexeme); if (value == nullptr) { - throw Error("Error interpreting: " + AstPrinter().print(expr) + - "Undefined property '" + expr->name.lexeme + "'"); + throw Error("Error interpreting: " + prettyPrint() + + "Undefined property '" + name.lexeme + "'"); } // std::cerr << "Returning from get: " << value->toString() << std::endl; return value; } -std::any Interpreter::visitSetExpr(std::shared_ptr expr) { - std::shared_ptr object = - std::any_cast>(expr->object->accept(*this)); - if (object == nullptr) { +std::shared_ptr Set::eval(Interpreter &interp) { + std::shared_ptr object_val = + object->eval(interp); + if (object_val == nullptr) { throw Error("Cannot set property on null"); } std::shared_ptr instance = - std::dynamic_pointer_cast(object); + std::dynamic_pointer_cast(object_val); if (instance == nullptr) { throw Error("Only instances have fields"); } - std::shared_ptr value = - std::any_cast>(expr->value->accept(*this)); - instance->set(expr->name.lexeme, value); - return value; + std::shared_ptr value_val = + value->eval(interp); + instance->set(name.lexeme, value_val); + return value_val; } -std::any Interpreter::visitLambdaExpr(std::shared_ptr expr) { - AstPrinter printer; +std::shared_ptr Lambda::eval(Interpreter &interp) { // std::cerr << "Visiting lambda: " - // << std::any_cast(expr->accept(printer)) << + // << std::get(accept(printer)) << // std::endl; - std::shared_ptr closure = environment; /// TODO: Fix if bug + std::shared_ptr closure = interp.environment; /// TODO: Fix if bug std::shared_ptr lambda = - std::make_shared(expr, closure); + std::make_shared(*this, closure); if (lambda == nullptr) { throw Error("visitingLambdaExpr:: Error creating lambda"); } @@ -754,171 +501,161 @@ std::any Interpreter::visitLambdaExpr(std::shared_ptr expr) { } } -std::any -Interpreter::visitTypeVariableExpr(std::shared_ptr expr) { - return environment->getTypeValue(expr->name); +std::shared_ptr TypeVariable::eval(Interpreter &interp) { + // TypeVariable is handled by resolveTypeExpr in type declaration contexts. + // If called directly as a value, try to find a class constructor. + auto type = interp.environment->getTypeValue(nameId); + if (type != nullptr) { + auto cls = std::dynamic_pointer_cast(type); + if (cls) return std::make_shared(cls); + } + throw Error("Type '" + name.lexeme + "' cannot be used as a value"); } -std::any Interpreter::visitTypeDeclExpr(std::shared_ptr expr) { - std::shared_ptr type = nullptr; - try { - type = std::any_cast>( - expr->typeexpr->accept(*this)); - } catch (const std::bad_any_cast &e) { - throw Error("TypeDecl must have a type" + - AstPrinter().print(expr->typeexpr)); - } - if (stateStack.size() != 0 && stateStack.top() == InterpretingState::OBJECT) { - return std::make_pair<>(expr->name.lexeme, type); +// Default: evaluate and extract either an AutumnClassValue's class or the +// value's runtime type. Specialized for ListTypeExpr and TypeVariable. +std::shared_ptr Expr::resolveTypeExpr(Interpreter &interp) { + return eval(interp)->getType(); +} - } else { - // std::cerr << "TypeDecl " << expr->name.lexeme << " " << type->toString() - // << std::endl; - environment->assignType(expr->name.lexeme, type); - return nullptr; - } +std::shared_ptr ListTypeExpr::resolveTypeExpr(Interpreter &interp) { + auto innerType = typeexpr->resolveTypeExpr(interp); + return AutumnListType::getInstance(innerType); } -std::any -Interpreter::visitListTypeExprExpr(std::shared_ptr expr) { - try { - std::shared_ptr ntype = - std::any_cast>( - expr->typeexpr->accept(*this)); - if (ntype == nullptr) { - throw Error("List type must have a type, instead got" + AstPrinter().print(expr)); - } - return std::shared_ptr(AutumnListType::getInstance(ntype)); - } catch (const std::bad_any_cast &e) { - throw Error("List type must have a type" + AstPrinter().print(expr)); - } +std::shared_ptr TypeVariable::resolveTypeExpr(Interpreter &interp) { + auto type = interp.getEnvironment()->getTypeValue(name); + if (type) return type; + throw Error("Undefined type '" + name.lexeme + "'"); } -std::any Interpreter::visitListVarExprExpr(std::shared_ptr expr) { +std::shared_ptr TypeDecl::eval(Interpreter &interp) { + auto type = typeexpr->resolveTypeExpr(interp); + interp.environment->assignType(nameId, type); + return nullptr; +} + +std::pair> TypeDecl::evalAsFieldDecl(Interpreter &interp) { + auto type = typeexpr->resolveTypeExpr(interp); + return std::make_pair(name.lexeme, type); +} + +std::shared_ptr ListTypeExpr::eval(Interpreter &interp) { + throw Error("ListTypeExpr cannot be evaluated as a value"); +} + +std::shared_ptr ListVarExpr::eval(Interpreter &interp) { try { auto pVarExprs = std::make_shared>>(); - for (auto subexpr : expr->varExprs) { + for (auto subexpr : varExprs) { try { - auto valueAny = subexpr->accept(*this); + auto valueAny = subexpr->eval(interp); auto autumnValue = - std::any_cast>(valueAny); + valueAny; pVarExprs->push_back(autumnValue); } catch (const std::bad_any_cast &e) { throw Error("visitListVarExprExpr Failed To Interpret " + - AstPrinter().print(subexpr)); + subexpr->prettyPrint()); } } auto plist = std::make_shared(pVarExprs); return std::dynamic_pointer_cast(plist); } catch (const std::bad_any_cast &e) { - throw Error("List must have values,got " + AstPrinter().print(expr)); + throw Error("List must have values,got " + prettyPrint()); } } -std::any Interpreter::visitIfExprExpr(std::shared_ptr expr) { - std::shared_ptr condition = nullptr; - try { - condition = - std::any_cast>( - expr->condition->accept(*this)); - - } catch (const std::bad_any_cast &e) { - throw Error("If condition must be a boolean expression: " + - AstPrinter().print(expr->condition) + " - " + e.what()); - } +std::shared_ptr IfExpr::eval(Interpreter &interp) { + std::shared_ptr condVal = this->condition->eval(interp); bool isCondTruthy = false; try { - isCondTruthy = condition->isTruthy(); + isCondTruthy = condVal->isTruthy(); } catch (const Error &e) { throw Error("If condition must be a boolean expression, instead got value: " + - condition->toString() + " - " + e.what()); + condVal->toString() + " - " + e.what()); } if (isCondTruthy) { try { - return expr->thenBranch->accept(*this); + return thenBranch->eval(interp); } catch (const Error &e) { - throw Error("If then branch error: " + AstPrinter().print(expr->thenBranch) + + throw Error("If then branch error: " + thenBranch->prettyPrint() + " - " + std::string(e.what())); } } else { try { - return expr->elseBranch->accept(*this); + return elseBranch->eval(interp); } catch (const Error &e) { - throw Error("If else branch error: " + AstPrinter().print(expr->elseBranch) + + throw Error("If else branch error: " + elseBranch->prettyPrint() + " - " + std::string(e.what())); } } } -std::any Interpreter::visitLetExpr(std::shared_ptr expr) { - auto subEnv = std::make_shared(environment); - setEnvironment(subEnv); +std::shared_ptr Let::eval(Interpreter &interp) { + auto subEnv = std::make_shared(interp.environment); + interp.setEnvironment(subEnv); std::shared_ptr value = nullptr; - std::any ret; - for (const auto &subexpr : expr->exprs) { - ret = subexpr->accept(*this); + std::shared_ptr ret; + for (const auto &subexpr : exprs) { + ret = subexpr->eval(interp); } try { - value = std::any_cast>(ret); + value = ret; } catch (const std::bad_any_cast &e) { - throw Error("Error visiting Let Expr- " + AstPrinter().print(expr) + + throw Error("Error visiting Let Expr- " + prettyPrint() + "\nGot:" + e.what()); } - setEnvironment(subEnv->getEnclosing()); + interp.setEnvironment(subEnv->getEnclosing()); return value; } -std::any Interpreter::visitInitNextExpr(std::shared_ptr expr) { +std::shared_ptr InitNext::eval(Interpreter &interp) { return nullptr; } -std::any Interpreter::visitObjectStmt(std::shared_ptr stmt) { - stateStack.push(InterpretingState::OBJECT); - // std::cerr << "Visiting object stmt " << AstPrinter().print(stmt) << - // std::endl; - std::vector>> fields; - for (const auto &field : stmt->fields) { - fields.push_back( - std::any_cast>>( - field->accept(*this))); +void Object::exec(Interpreter &interp) { + interp.stateStack.push(Interpreter::OBJECT); + std::vector>> fieldDecls; + for (const auto &field : this->fields) { + auto typeDecl = std::dynamic_pointer_cast(field); + if (typeDecl) { + fieldDecls.push_back(typeDecl->evalAsFieldDecl(interp)); + } } - environment->defineType( - stmt->name.lexeme, - makeObjectClass(*this, stmt->name.lexeme, fields, stmt->Cell)); - stateStack.pop(); - // std::cerr << "Defined object " << stmt->name.lexeme << " with type " - // << std::any_cast>( - // environment->getTypeValue(stmt->name)) + auto clsType = makeObjectClass(interp, name.lexeme, fieldDecls, Cell); + interp.environment->defineType(nameId, clsType); + interp.stateStack.pop(); + // std::cerr << "Defined object " << name.lexeme << " with type " + // << std::get>( + // interp.environment->getTypeValue(name)) // ->toString() // << std::endl; - return nullptr; } -std::any Interpreter::visitBlockStmt(std::shared_ptr stmt) { +void Block::exec(Interpreter &interp) { throw Error("Block statement is not allowed in this language"); } -std::any Interpreter::visitExpressionStmt(std::shared_ptr stmt) { - std::any value = stmt->expression->accept(*this); - return nullptr; +void Expression::exec(Interpreter &interp) { + expression->eval(interp); } -std::any Interpreter::visitOnStmtStmt(std::shared_ptr stmt) { - onStmts.push_back(stmt); - return nullptr; +void OnStmt::exec(Interpreter &interp) { + interp.onStmts.push_back(std::make_shared(condition, expr)); } void Interpreter::start(const std::vector> &stmts, - std::string stdlib, std::string triggeringCondition, + std::string stdlib, std::string triggeringCondition, uint64_t randomSeed) { + auto &interp = *this; setRandomSeed(randomSeed); init(stdlib); environment->assign("SpecialConditionTriggered", std::make_shared(false)); if (triggeringCondition != "") { try { - SExpParser parser(triggeringCondition); + SExpParser parser(triggeringCondition, interner_); std::shared_ptr expr = parser.parseExpr(sexpresso::parse(triggeringCondition)); this->triggeringConditionExpr = expr; @@ -928,30 +665,28 @@ void Interpreter::start(const std::vector> &stmts, } } for (const auto &stmt : stmts) { - stmt->accept(*this); + stmt->exec(interp); } - // Then start visiting initExpr - for (const auto &[k, v] : initMap) { + // Evaluate init expressions in declaration order. + for (const auto &var : initNextVars) { + if (var.initExpr == nullptr) continue; try { - std::shared_ptr value = - std::any_cast>(v->accept(*this)); - std::string name = k; + std::shared_ptr value = var.initExpr->eval(interp); + const std::string &name = *var.name; if (value == nullptr) { throw Error("Inititalizing: " + name + ": " + "must have a value instead got null ptr"); } - // Fetch the corresponding type if (getVerbose()) { std::cerr << "Init " << name << ": " << std::endl - << value->toString() << std::endl; - // flush stderr - std::cerr << std::endl; + << value->toString() << std::endl + << std::endl; } - std::shared_ptr type = environment->getAssignedType(name); - environment->define(name, value); + std::shared_ptr type = environment->getAssignedType(var.name); + environment->define(var.name, value); } catch (const std::bad_any_cast &e) { - throw Error("Init must have a value: " + AstPrinter().print(v)); + throw Error("Init must have a value: " + var.initExpr->prettyPrint()); } catch (const RuntimeError &e) { if (getVerbose()) { std::cerr << "Warning:: Exception " << e.token.lexeme << std::endl; @@ -965,6 +700,7 @@ void Interpreter::start(const std::vector> &stmts, } void Interpreter::tmpExecuteStmt(const std::shared_ptr &stmt) { + auto &interp = *this; try { cacheEnvironment(environment); std::shared_ptr exprStmt = std::dynamic_pointer_cast(stmt); @@ -973,11 +709,11 @@ void Interpreter::tmpExecuteStmt(const std::shared_ptr &stmt) { std::shared_ptr assign = std::dynamic_pointer_cast(exprStmt->expression); if (assign != nullptr) { // visit this - visitAssignExpr(assign); + assign->eval(interp); std::shared_ptr initNext = std::dynamic_pointer_cast(assign->value); if (initNext != nullptr) { - environment->define(assign->name.lexeme, - std::any_cast>(initNext->initializer->accept(*this))); + environment->define(assign->name.lexeme, + initNext->initializer->eval(interp)); } return; } @@ -986,19 +722,20 @@ void Interpreter::tmpExecuteStmt(const std::shared_ptr &stmt) { return; } else{ - stmt->accept(*this); - } + stmt->exec(interp); + } }catch (const Error &e) { throw Error("Error in tmpExecuteStmt: " + std::string(e.what())); } } void Interpreter::step() { + auto &interp = *this; // Copy previous globals // Delete previous environment auto old_prev_environment = prev_environment; if (prev_environment == nullptr) { - prev_environment = std::make_shared(); + prev_environment = std::make_shared(interner_); } // Copy the stack std::stack> stack; @@ -1020,8 +757,8 @@ void Interpreter::step() { // Execute triggeringCondition if (this->triggeringConditionExpr != nullptr) { std::shared_ptr condition = - std::any_cast>( - this->triggeringConditionExpr->accept(*this)); + + this->triggeringConditionExpr->eval(interp); if (!condition->isTruthy()) { environment->assign("SpecialConditionTriggered", std::make_shared(false)); @@ -1032,14 +769,14 @@ void Interpreter::step() { } // Execute onStmts std::vector> updated; - AstPrinter printer; for (int i = 0; i < onStmts.size(); i++) { auto stmt = onStmts[i]; std::shared_ptr condition = nullptr; std::shared_ptr onStmt = std::dynamic_pointer_cast(stmt); try { - condition = std::any_cast>( - onStmt->condition->accept(*this)); + condition = + onStmt->condition->eval(interp); + std::cerr << "On condition: " << condition->toString() << std::endl; } catch (const std::bad_any_cast &e) { throw Error("On condition must be a value"); } @@ -1061,25 +798,19 @@ void Interpreter::step() { if (condEval->isTruthy()) { onClauseCovered.insert(i); std::shared_ptr value = - std::any_cast>( - onStmt->expr->accept(*this)); + + onStmt->expr->eval(interp); } } - for (auto kvPairs : nextMap) { - std::string key = kvPairs.first; - std::shared_ptr nextExpr = kvPairs.second; - if (environment->isUpdated(key)) { - continue; - } - auto allDefineds = environment->getDefinedVariables(); - if (allDefineds.find(key) == allDefineds.end()) { - continue; - } - std::shared_ptr value = - std::any_cast>(nextExpr->accept(*this)); - environment->assign(key, value); + for (const auto &var : initNextVars) { + if (var.nextExpr == nullptr) continue; + if (environment->isUpdated(var.name)) continue; + auto &allDefineds = environment->getDefinedVariables(); + if (allDefineds.find(var.name) == allDefineds.end()) continue; + std::shared_ptr value = var.nextExpr->eval(interp); + environment->assign(var.name, value); } - this->state->reset(); + state.reset(); } static std::shared_ptr @@ -1104,7 +835,7 @@ renderValue(Interpreter &interpreter, std::shared_ptr value) { } std::shared_ptr render = std::dynamic_pointer_cast( - pInstance->get("render")); + AutumnInstance::getWithMethod(pInstance, "render")); std::shared_ptr result = render->call(interpreter, {}); return std::dynamic_pointer_cast(result); } else { @@ -1120,15 +851,14 @@ std::string Interpreter::renderAll() { result.reserve(10000); auto allDefineds = environment->getDefinedVariables(); auto vars = environment->getDefinitionOrder(); - auto visited = std::unordered_set(); - visited.reserve(initOrder.size() + vars.size()); - // copy vars to initOrder - auto renderOrder = std::vector(); - renderOrder.reserve(initOrder.size() + vars.size()); - for (auto &elem : initOrder) { - if (visited.find(elem) == visited.end()) { - renderOrder.push_back(elem); - visited.insert(elem); + auto visited = std::unordered_set(); + visited.reserve(initNextVars.size() + vars.size()); + auto renderOrder = std::vector(); + renderOrder.reserve(initNextVars.size() + vars.size()); + for (const auto &var : initNextVars) { + if (visited.find(var.name) == visited.end()) { + renderOrder.push_back(var.name); + visited.insert(var.name); } } for (auto &elem : vars) { @@ -1140,7 +870,7 @@ std::string Interpreter::renderAll() { auto pAllElems = std::make_shared(); pAllElems->getValues()->reserve(10000); static constexpr const char* ELEMENTS_TEMPLATE = "\"%s\": ["; - static constexpr const char* POSITION_TEMPLATE = + static constexpr const char* POSITION_TEMPLATE = "{\"position\": {\"x\": %d, \"y\": %d}, \"color\": %s}, "; for (auto k : renderOrder) { auto pList = renderValue(*this, allDefineds[k]); @@ -1148,7 +878,7 @@ std::string Interpreter::renderAll() { continue; } char buffer[256]; - snprintf(buffer, sizeof(buffer), ELEMENTS_TEMPLATE, k.c_str()); + snprintf(buffer, sizeof(buffer), ELEMENTS_TEMPLATE, k->c_str()); result += buffer; for (auto &elem : *(pList->getValues())) { auto elemInstance = std::dynamic_pointer_cast(elem); @@ -1164,7 +894,7 @@ std::string Interpreter::renderAll() { std::dynamic_pointer_cast(elemInstance->get("color")) ->getString(); char buffer[256]; - snprintf(buffer, sizeof(buffer), POSITION_TEMPLATE, + snprintf(buffer, sizeof(buffer), POSITION_TEMPLATE, x, y, color.c_str()); result += buffer; } @@ -1200,9 +930,10 @@ int Interpreter::getFrameRate() { std::string Interpreter::evaluateToString(std::string expr) { - SExpParser parser(expr); + auto &interp = *this; + SExpParser parser(expr, interner_); auto parsedExpr = parser.parseExpr(sexpresso::parse(expr)); - auto result = std::any_cast>(parsedExpr->accept(*this)); + auto result = parsedExpr->eval(interp); if (result == nullptr) { return "NULL"; } diff --git a/src/interpreter/stdlib/Any.cpp b/src/interpreter/stdlib/Any.cpp index 08f1389..4a7a807 100644 --- a/src/interpreter/stdlib/Any.cpp +++ b/src/interpreter/stdlib/Any.cpp @@ -23,9 +23,6 @@ class IdentityFunction : public AutumnCallable { } int arity() override { return 1; } std::string toString() const override { return ""; } - std::shared_ptr clone() override { - return std::make_shared(); - } }; std::shared_ptr diff --git a/src/interpreter/stdlib/Clicked.cpp b/src/interpreter/stdlib/Clicked.cpp index ee55787..f3b5f3c 100644 --- a/src/interpreter/stdlib/Clicked.cpp +++ b/src/interpreter/stdlib/Clicked.cpp @@ -40,7 +40,7 @@ renderValue(Interpreter &interpreter, std::shared_ptr value) { } std::shared_ptr render = std::dynamic_pointer_cast( - pInstance->get("render")); + AutumnInstance::getWithMethod(pInstance, "render")); std::shared_ptr result = render->call(interpreter, {}); return std::dynamic_pointer_cast(result); } else { @@ -53,7 +53,7 @@ renderValue(Interpreter &interpreter, std::shared_ptr value) { std::shared_ptr Clicked::call(Interpreter &interpreter, const std::vector> &arguments) { - if (interpreter.getState()->getClicked() == false) { + if (interpreter.getState().getClicked() == false) { return std::make_shared(false); } interpreter.getGlobals()->define( @@ -61,9 +61,9 @@ Clicked::call(Interpreter &interpreter, std::make_shared( PositionClass, std::vector>( - {std::make_shared(interpreter.getState()->getX()), + {std::make_shared(interpreter.getState().getX()), std::make_shared( - interpreter.getState()->getY())}))); + interpreter.getState().getY())}))); if (arguments.size() == 0) { return std::make_shared(true); } @@ -73,8 +73,8 @@ Clicked::call(Interpreter &interpreter, // Filter out the element that is clicked std::string positionString = std::string("Position ") + - std::to_string(interpreter.getState()->getX()) + " " + - std::to_string(interpreter.getState()->getY()); + std::to_string(interpreter.getState().getX()) + " " + + std::to_string(interpreter.getState().getY()); // std::cout << "Clicked at " << positionString << std::endl; // std::cout << "Object to be tested: " << renderedElem->toString() // << std::endl; @@ -98,8 +98,8 @@ Clicked::call(Interpreter &interpreter, ->getNumber(); auto y = std::dynamic_pointer_cast(position->get("y")) ->getNumber(); - if (x == interpreter.getState()->getX() && - y == interpreter.getState()->getY()) { + if (x == interpreter.getState().getX() && + y == interpreter.getState().getY()) { return std::make_shared(true); } } diff --git a/src/interpreter/stdlib/Defined.cpp b/src/interpreter/stdlib/Defined.cpp index 902180e..8e07eb7 100644 --- a/src/interpreter/stdlib/Defined.cpp +++ b/src/interpreter/stdlib/Defined.cpp @@ -23,8 +23,7 @@ Defined::call(Interpreter &Interpreter, if (varName[0] == '\"') { varName = varName.substr(1, varName.size() - 2); } - auto defineds = Interpreter.getGlobals()->getDefinedVariables(); - if (defineds.find(varName) != defineds.end()) { + if (Interpreter.getGlobals()->isDefined(varName)) { return std::make_shared(true); } return std::make_shared(false); diff --git a/src/interpreter/stdlib/DownPressed.cpp b/src/interpreter/stdlib/DownPressed.cpp index 343ce3c..bc4ee74 100644 --- a/src/interpreter/stdlib/DownPressed.cpp +++ b/src/interpreter/stdlib/DownPressed.cpp @@ -1,23 +1,16 @@ -#include "AutumnCallableValue.hpp" -#include "AutumnInstance.hpp" #include "AutumnStdComponents.hpp" #include "AutumnStdLib.hpp" #include "AutumnValue.hpp" -#include "Environment.hpp" #include "Interpreter.hpp" -#include "Parser.hpp" -#include "sexpresso.hpp" #include -#include #include #include -#include namespace Autumn { std::shared_ptr DownPressed::call(Interpreter &interpreter, const std::vector> &arguments) { - return std::make_shared(interpreter.getState()->getDown()); + return std::make_shared(interpreter.getState().getDown()); }; int DownPressed::arity() { return 2; } diff --git a/src/interpreter/stdlib/IsOutsideBounds.cpp b/src/interpreter/stdlib/IsOutsideBounds.cpp index b140640..b039f5b 100644 --- a/src/interpreter/stdlib/IsOutsideBounds.cpp +++ b/src/interpreter/stdlib/IsOutsideBounds.cpp @@ -23,8 +23,7 @@ std::shared_ptr IsOutSideBounds::call( std::dynamic_pointer_cast( callable->call(interpreter, arguments)); // Invert the result - return std::make_shared( - !std::any_cast(isWithinBoundRet->getValue())); + return std::make_shared(!isWithinBoundRet->getBool()); } int IsOutSideBounds::arity() { return 1; } diff --git a/src/interpreter/stdlib/IsWithinBounds.cpp b/src/interpreter/stdlib/IsWithinBounds.cpp index 1f7d672..7ecb6a4 100644 --- a/src/interpreter/stdlib/IsWithinBounds.cpp +++ b/src/interpreter/stdlib/IsWithinBounds.cpp @@ -35,7 +35,7 @@ renderValue(Interpreter &interpreter, std::shared_ptr value) { } std::shared_ptr render = std::dynamic_pointer_cast( - pInstance->get("render")); + AutumnInstance::getWithMethod(pInstance, "render")); std::shared_ptr result = render->call(interpreter, {}); return std::dynamic_pointer_cast(result); } else { diff --git a/src/interpreter/stdlib/LeftPressed.cpp b/src/interpreter/stdlib/LeftPressed.cpp index 22d0a70..44160d6 100644 --- a/src/interpreter/stdlib/LeftPressed.cpp +++ b/src/interpreter/stdlib/LeftPressed.cpp @@ -1,23 +1,16 @@ -#include "AutumnCallableValue.hpp" -#include "AutumnInstance.hpp" #include "AutumnStdComponents.hpp" #include "AutumnStdLib.hpp" #include "AutumnValue.hpp" -#include "Environment.hpp" #include "Interpreter.hpp" -#include "Parser.hpp" -#include "sexpresso.hpp" #include -#include #include #include -#include namespace Autumn { std::shared_ptr LeftPressed::call(Interpreter &interpreter, const std::vector> &arguments) { - return std::make_shared(interpreter.getState()->getLeft()); + return std::make_shared(interpreter.getState().getLeft()); }; int LeftPressed::arity() { return 2; } diff --git a/src/interpreter/stdlib/RenderAll.cpp b/src/interpreter/stdlib/RenderAll.cpp index 26ce088..c692fdc 100644 --- a/src/interpreter/stdlib/RenderAll.cpp +++ b/src/interpreter/stdlib/RenderAll.cpp @@ -35,7 +35,7 @@ renderValue(Interpreter &interpreter, std::shared_ptr value) { } std::shared_ptr render = std::dynamic_pointer_cast( - pInstance->get("render")); + AutumnInstance::getWithMethod(pInstance, "render")); std::shared_ptr result = render->call(interpreter, {}); return std::dynamic_pointer_cast(result); } else { diff --git a/src/interpreter/stdlib/RightPressed.cpp b/src/interpreter/stdlib/RightPressed.cpp index 9281d08..b4f4cc9 100644 --- a/src/interpreter/stdlib/RightPressed.cpp +++ b/src/interpreter/stdlib/RightPressed.cpp @@ -1,23 +1,16 @@ -#include "AutumnCallableValue.hpp" -#include "AutumnInstance.hpp" #include "AutumnStdComponents.hpp" #include "AutumnStdLib.hpp" #include "AutumnValue.hpp" -#include "Environment.hpp" #include "Interpreter.hpp" -#include "Parser.hpp" -#include "sexpresso.hpp" #include -#include #include #include -#include namespace Autumn { std::shared_ptr RightPressed::call(Interpreter &interpreter, const std::vector> &arguments) { - return std::make_shared(interpreter.getState()->getRight()); + return std::make_shared(interpreter.getState().getRight()); }; int RightPressed::arity() { return 2; } diff --git a/src/interpreter/stdlib/Rotate.cpp b/src/interpreter/stdlib/Rotate.cpp index c5b0e73..cfb168c 100644 --- a/src/interpreter/stdlib/Rotate.cpp +++ b/src/interpreter/stdlib/Rotate.cpp @@ -42,7 +42,7 @@ Rotate::call(Interpreter &interpreter, continue; subenv->define(fieldname, instance->get(fieldname)); } - SExpParser tmpParser = SExpParser(""); + SExpParser tmpParser = SExpParser("", interpreter.getInterner()); for (auto &value : *list->getValues()) { auto subinstance = std::dynamic_pointer_cast(value); if (subinstance == nullptr) { diff --git a/src/interpreter/stdlib/UpPressed.cpp b/src/interpreter/stdlib/UpPressed.cpp index adc69d7..60319ff 100644 --- a/src/interpreter/stdlib/UpPressed.cpp +++ b/src/interpreter/stdlib/UpPressed.cpp @@ -1,23 +1,16 @@ -#include "AutumnCallableValue.hpp" -#include "AutumnInstance.hpp" #include "AutumnStdComponents.hpp" #include "AutumnStdLib.hpp" #include "AutumnValue.hpp" -#include "Environment.hpp" #include "Interpreter.hpp" -#include "Parser.hpp" -#include "sexpresso.hpp" #include -#include #include #include -#include namespace Autumn { std::shared_ptr UpPressed::call(Interpreter &interpreter, const std::vector> &arguments) { - return std::make_shared(interpreter.getState()->getUp()); + return std::make_shared(interpreter.getState().getUp()); }; int UpPressed::arity() { return 2; } diff --git a/src/interpreter/stdlib/isFreePos.cpp b/src/interpreter/stdlib/isFreePos.cpp index 68a630d..870337d 100644 --- a/src/interpreter/stdlib/isFreePos.cpp +++ b/src/interpreter/stdlib/isFreePos.cpp @@ -27,8 +27,13 @@ IsFreePos::call(Interpreter &interpreter, if (pInstance->getClass() != PositionClass) { throw Error("isFreePos requires a Position instance as argument"); } - int x = std::any_cast(pInstance->get("x")->getValue()); - int y = std::any_cast(pInstance->get("y")->getValue()); + auto xNum = std::dynamic_pointer_cast(pInstance->get("x")); + auto yNum = std::dynamic_pointer_cast(pInstance->get("y")); + if (!xNum || !yNum) { + throw Error("isFreePos: Position x and y must be numbers"); + } + int x = xNum->getNumber(); + int y = yNum->getNumber(); return std::make_shared( interpreter.getGlobals()->isFreePos(x, y)); } diff --git a/src/interpreter/typesystem/AutumnType.cpp b/src/interpreter/typesystem/AutumnType.cpp index 06d44b6..56f23cf 100644 --- a/src/interpreter/typesystem/AutumnType.cpp +++ b/src/interpreter/typesystem/AutumnType.cpp @@ -1,8 +1,6 @@ #include "AutumnType.hpp" namespace Autumn { -std::shared_ptr AutumnNumberType::instance = nullptr; -std::shared_ptr AutumnStringType::instance = nullptr; -std::shared_ptr AutumnBoolType::instance = nullptr; -std::shared_ptr AutumnUnknownType::instance = nullptr; +// All AutumnType singletons are now function-local magic statics inside +// getInstance(), so no out-of-class definitions are required here. } // namespace Autumn diff --git a/src/interpreter/valuesystem/AutumnClass.cpp b/src/interpreter/valuesystem/AutumnClass.cpp index 0921d8b..33373da 100644 --- a/src/interpreter/valuesystem/AutumnClass.cpp +++ b/src/interpreter/valuesystem/AutumnClass.cpp @@ -20,9 +20,7 @@ AutumnMethod::call(Interpreter &interpreter, environment->define(fieldname, instance->get(fieldname)); } // Execute the lambda's body within the new environment - std::shared_ptr retVal = - std::any_cast>( - callable->call(interpreter, arguments)); + std::shared_ptr retVal = callable->call(interpreter, arguments); // Restore the previous environment interpreter.setEnvironment(environment->getEnclosing()); return retVal; diff --git a/src/interpreter/valuesystem/AutumnExprValue.cpp b/src/interpreter/valuesystem/AutumnExprValue.cpp index 482a7f3..b4d8a82 100644 --- a/src/interpreter/valuesystem/AutumnExprValue.cpp +++ b/src/interpreter/valuesystem/AutumnExprValue.cpp @@ -1,23 +1,29 @@ #include "AutumnExprValue.hpp" -#include "AstPrinter.hpp" namespace Autumn { -std::shared_ptr AutumnExprType::instance = nullptr; - std::string AutumnExprValue::toString() const { try { - return std::string("(" + AstPrinter().print(cexpr) + ": Expr)"); + return std::string("(" + cexpr->prettyPrint() + ": Expr)"); } catch (Error &e) { throw Error(std::string("Error Printing Expr value: ") + e.what()); } } -bool AutumnExprValue::isEqual(std::shared_ptr other) { - std::shared_ptr otherExpr = - std::dynamic_pointer_cast(other); - if (otherExpr == nullptr) { - return false; +std::shared_ptr +AutumnExprValue::binop_on(AutumnValue &rhs, const Token &op) { + switch (op.type) { + case TokenType::EQUAL_EQUAL: + return std::make_shared(equal_to(rhs)); + case TokenType::BANG_EQUAL: + return std::make_shared(!equal_to(rhs)); + default: + throw Error("Operator not supported on expr values"); } - return cexpr == otherExpr->cexpr; +} + +bool AutumnExprValue::equal_to(AutumnValue &rhs) { return rhs.equal_by_expr(*this); } + +bool AutumnExprValue::equal_by_expr(AutumnExprValue &lhs) { + return lhs.cexpr == cexpr; } } // namespace Autumn diff --git a/src/interpreter/valuesystem/AutumnValue.cpp b/src/interpreter/valuesystem/AutumnValue.cpp index 85bf67e..e21f69f 100644 --- a/src/interpreter/valuesystem/AutumnValue.cpp +++ b/src/interpreter/valuesystem/AutumnValue.cpp @@ -4,93 +4,209 @@ namespace Autumn { int AutumnValue::instCount = 0; -bool AutumnNumber::isEqual(std::shared_ptr other) { - if (other == nullptr) { - return false; - } - if (std::dynamic_pointer_cast(other) == nullptr) { - if (std::dynamic_pointer_cast(other) == nullptr) { - return false; - } - return getNumber() == - (std::dynamic_pointer_cast(other)->isTruthy() ? 1 : 0); - } +// --------------------------------------------------------------------------- +// Unary +// --------------------------------------------------------------------------- - return getNumber() == - std::dynamic_pointer_cast(other)->getNumber(); +std::shared_ptr AutumnValue::eval_unary(const Token &op) { + switch (op.type) { + case TokenType::MINUS: + throw Error("Unary - must be applied to a number"); + case TokenType::PLUS: + throw Error("Unary + must be applied to a number"); + case TokenType::BANG: + throw Error("Unary ! must be applied to a boolean"); + default: + throw Error("Unknown unary operator"); + } } -bool AutumnString::isEqual(std::shared_ptr other) { - if (other == nullptr) { - return false; +std::shared_ptr +AutumnNumber::eval_unary(const Token &op) { + auto result = getNumber(); + switch (op.type) { + case TokenType::PLUS: + break; + case TokenType::MINUS: + result = -result; + break; + case TokenType::BANG: + throw Error("Unary ! must be applied to a boolean"); + default: + throw Error("Unknown unary operator"); } - if (std::dynamic_pointer_cast(other) == nullptr) { - return false; - } - return getString() == - std::dynamic_pointer_cast(other)->getString(); + return std::make_shared(result); } -bool AutumnBool::isEqual(std::shared_ptr other) { - if (other == nullptr) { - return false; +std::shared_ptr +AutumnBool::eval_unary(const Token &op) { + switch (op.type) { + case TokenType::BANG: + return std::make_shared(!getBool()); + case TokenType::MINUS: + throw Error("Unary - must be applied to a number"); + case TokenType::PLUS: + throw Error("Unary + must be applied to a number"); + default: + throw Error("Unknown unary operator"); } - if (std::dynamic_pointer_cast(other) == nullptr) { - return false; +} + +// --------------------------------------------------------------------------- +// AutumnNumber — the only type that supports arithmetic and ordering. +// --------------------------------------------------------------------------- + +std::shared_ptr +AutumnNumber::binop_on(AutumnValue &rhs, const Token &op) { + int lhs = getNumber(); + switch (op.type) { + case TokenType::PLUS: + return std::make_shared(rhs.add_by_number(lhs)); + case TokenType::MINUS: + return std::make_shared(rhs.sub_by_number(lhs)); + case TokenType::STAR: + return std::make_shared(rhs.mul_by_number(lhs)); + case TokenType::SLASH: + return std::make_shared(rhs.div_by_number(lhs)); + case TokenType::MODULO: + return std::make_shared(rhs.mod_by_number(lhs)); + case TokenType::GREATER: + return std::make_shared(rhs.greater_by_number(lhs)); + case TokenType::GREATER_EQUAL: + return std::make_shared(rhs.greater_equal_by_number(lhs)); + case TokenType::LESS: + return std::make_shared(rhs.less_by_number(lhs)); + case TokenType::LESS_EQUAL: + return std::make_shared(rhs.less_equal_by_number(lhs)); + case TokenType::EQUAL_EQUAL: + return std::make_shared(equal_to(rhs)); + case TokenType::BANG_EQUAL: + return std::make_shared(!equal_to(rhs)); + default: + throw Error("Unknown binary operator for number"); } - return isTruthy() == std::dynamic_pointer_cast(other)->isTruthy(); } -bool AutumnList::isEqual(std::shared_ptr other) { - if (other == nullptr) { - return false; +bool AutumnNumber::equal_to(AutumnValue &rhs) { return rhs.equal_by_number(getNumber()); } + +int AutumnNumber::add_by_number(int lhs) { return lhs + getNumber(); } +int AutumnNumber::sub_by_number(int lhs) { return lhs - getNumber(); } +int AutumnNumber::mul_by_number(int lhs) { return lhs * getNumber(); } +int AutumnNumber::div_by_number(int lhs) { return lhs / getNumber(); } +int AutumnNumber::mod_by_number(int lhs) { return lhs % getNumber(); } + +bool AutumnNumber::greater_by_number(int lhs) { return lhs > getNumber(); } +bool AutumnNumber::greater_equal_by_number(int lhs) { return lhs >= getNumber(); } +bool AutumnNumber::less_by_number(int lhs) { return lhs < getNumber(); } +bool AutumnNumber::less_equal_by_number(int lhs) { return lhs <= getNumber(); } + +bool AutumnNumber::equal_by_number(int lhs) { return lhs == getNumber(); } + +// --------------------------------------------------------------------------- +// AutumnString — only equality. +// --------------------------------------------------------------------------- + +std::shared_ptr +AutumnString::binop_on(AutumnValue &rhs, const Token &op) { + switch (op.type) { + case TokenType::EQUAL_EQUAL: + return std::make_shared(equal_to(rhs)); + case TokenType::BANG_EQUAL: + return std::make_shared(!equal_to(rhs)); + default: + throw Error("Operator not supported on strings"); } - if (std::dynamic_pointer_cast(other) == nullptr) { - return false; +} + +bool AutumnString::equal_to(AutumnValue &rhs) { return rhs.equal_by_string(getString()); } + +bool AutumnString::equal_by_string(const std::string &lhs) { return lhs == getString(); } + +// --------------------------------------------------------------------------- +// AutumnBool — only equality, with the bool↔number asymmetric coercion +// preserved from the legacy isEqual. +// --------------------------------------------------------------------------- + +std::shared_ptr +AutumnBool::binop_on(AutumnValue &rhs, const Token &op) { + switch (op.type) { + case TokenType::EQUAL_EQUAL: + return std::make_shared(equal_to(rhs)); + case TokenType::BANG_EQUAL: + return std::make_shared(!equal_to(rhs)); + default: + throw Error("Operator not supported on booleans"); } - return toString() == std::dynamic_pointer_cast(other)->toString(); } -bool AutumnNull::isEqual(std::shared_ptr other) { - if (other == nullptr) { - return false; +bool AutumnBool::equal_to(AutumnValue &rhs) { return rhs.equal_by_bool(getBool()); } + +bool AutumnBool::equal_by_bool(bool lhs) { return lhs == getBool(); } + +// Called when LHS is a number and RHS is a bool: coerce bool to 1/0. +bool AutumnBool::equal_by_number(int lhs) { return lhs == (getBool() ? 1 : 0); } + +// --------------------------------------------------------------------------- +// AutumnList — only equality; element-wise recursive via isEqual. +// --------------------------------------------------------------------------- + +std::shared_ptr +AutumnList::binop_on(AutumnValue &rhs, const Token &op) { + switch (op.type) { + case TokenType::EQUAL_EQUAL: + return std::make_shared(equal_to(rhs)); + case TokenType::BANG_EQUAL: + return std::make_shared(!equal_to(rhs)); + default: + throw Error("Operator not supported on lists"); } - if (std::dynamic_pointer_cast(other) == nullptr) { - return false; +} + +bool AutumnList::equal_to(AutumnValue &rhs) { return rhs.equal_by_list(*getValues()); } + +bool AutumnList::equal_by_list( + const std::vector> &lhs) { + auto &rhsVec = *getValues(); + if (lhs.size() != rhsVec.size()) return false; + for (size_t i = 0; i < lhs.size(); i++) { + if (!lhs[i]) { + if (rhsVec[i]) return false; + continue; + } + if (!lhs[i]->isEqual(rhsVec[i])) return false; } return true; } +// --------------------------------------------------------------------------- +// AutumnList::add — mutates the underlying vector in place. +// --------------------------------------------------------------------------- + void AutumnList::add(std::shared_ptr elem) { - try { - auto plist = std::any_cast< - std::shared_ptr>>>(value); - if (plist == nullptr) { - throw Error("AutumnList::add Error: List is null"); - } - if (elem == nullptr) { - throw Error("AutumnList::add Error: Element is null"); - } - auto instElem = std::dynamic_pointer_cast(elem); - if (elem->getType()->toString() != - std::dynamic_pointer_cast(type) - ->getElementType() - ->toString() && - std::dynamic_pointer_cast( - std::dynamic_pointer_cast(type) - ->getElementType()) == nullptr && - instElem == nullptr) { - throw Error("List element type mismatch: got " + - elem->getType()->toString() + " expected " + - std::dynamic_pointer_cast(type) - ->getElementType() - ->toString()); - } - this->type = AutumnListType::getInstance(elem->getType()); - plist->push_back(elem); - } catch (std::bad_any_cast &e) { - throw Error("AutumnList::add Error: " + std::string(e.what())); + auto plist = values; + if (plist == nullptr) { + throw Error("AutumnList::add Error: List is null"); + } + if (elem == nullptr) { + throw Error("AutumnList::add Error: Element is null"); + } + auto instElem = std::dynamic_pointer_cast(elem); + if (elem->getType()->toString() != + std::dynamic_pointer_cast(type) + ->getElementType() + ->toString() && + std::dynamic_pointer_cast( + std::dynamic_pointer_cast(type) + ->getElementType()) == nullptr && + instElem == nullptr) { + throw Error("List element type mismatch: got " + + elem->getType()->toString() + " expected " + + std::dynamic_pointer_cast(type) + ->getElementType() + ->toString()); } + this->type = AutumnListType::getInstance(elem->getType()); + plist->push_back(elem); } } // namespace Autumn diff --git a/test2.jl b/test2.jl index 72e2556..ea30f27 100644 --- a/test2.jl +++ b/test2.jl @@ -101,7 +101,7 @@ function get_cells_from_interpreter(interpreter::Interpreter) end interpreter = Interpreter() -program = read(joinpath(@__DIR__, "tests/grow.sexp"), String) +program = read(joinpath(@__DIR__, "tests/balloon.sexp"), String) run_script(interpreter, program, read(joinpath(@__DIR__, "autumnstdlib/stdlib.sexp"), String), "") step(interpreter) step(interpreter) @@ -131,4 +131,4 @@ goal_state, grid_size = get_cells_from_interpreter(interpreter) println(goal_state) println(grid_size) -render_frame(goal_state, grid_size) +render_frame(goal_state, grid_size) \ No newline at end of file diff --git a/testInterpreters/test_grow.cpp b/testInterpreters/test_grow.cpp deleted file mode 100644 index 6aa79ef..0000000 --- a/testInterpreters/test_grow.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "AstPrinter.hpp" -#include "AutumnClass.hpp" -#include "Interpreter.hpp" -#include "Parser.hpp" -#include -#include -#include -#include -#include -#include - -std::string readFile(const std::string &filename) { - std::ifstream file(filename); - std::stringstream buffer; - buffer << file.rdbuf(); - return buffer.str(); -} - -int main(int argc, char **argv) { - std::string mysexpr = readFile("../tests/grow.sexp"); - Autumn::SExpParser parser(mysexpr); - std::vector> stmts = parser.parseStmt(); - Autumn::Interpreter interpreter; - try { - interpreter.start(stmts); - } catch (const std::runtime_error &e) { - std::cerr << e.what() << std::endl; - } - - // LOAD """""" - // EVAL - // - // CLICK - // LEFT - // RIGHT - // UP - // DOWN - // - // EVAL - - std::cout << interpreter.renderAll() << std::endl; - - interpreter.getState()->pushLeft(); - interpreter.step(); - interpreter.getState()->click(0, 0); - interpreter.step(); - interpreter.getState()->click(0, 0); - interpreter.step(); - // std::cout << std::endl << std::endl; - // std::cout << "Lights after click: " << std::endl; - // std::cout << interpreter.getEnvironment()->get("lights")->toString() - // << std::endl; - - std::cout << interpreter.renderAll() << std::endl; - // std::cerr << "Rendered ants: " << renderedAnts->toString() << std::endl; - // interpreter.step(); - return 0; -} diff --git a/testInterpreters/test_light.cpp b/testInterpreters/test_light.cpp deleted file mode 100644 index 50b1c4f..0000000 --- a/testInterpreters/test_light.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "AstPrinter.hpp" -#include "AutumnClass.hpp" -#include "Interpreter.hpp" -#include "Parser.hpp" -#include -#include -#include -#include -#include -#include - -std::string readFile(const std::string &filename) { - std::ifstream file(filename); - std::stringstream buffer; - buffer << file.rdbuf(); - return buffer.str(); -} - -int main(int argc, char **argv) { - std::string mysexpr = readFile("../tests/test2.sexp"); - Autumn::SExpParser parser(mysexpr); - std::vector> stmts = parser.parseStmt(); - Autumn::Interpreter interpreter; - try { - interpreter.start(stmts); - } catch (const std::runtime_error &e) { - std::cerr << e.what() << std::endl; - } - - std::cout << interpreter.renderAll() << std::endl; - interpreter.getState()->click(0, 0); - interpreter.step(); - std::cout << interpreter.renderAll() << std::endl; - - // std::cerr << "Rendered ants: " << renderedAnts->toString() << std::endl; - // interpreter.step(); - return 0; -} diff --git a/tools/bindings.cpp b/tools/bindings.cpp index 990df96..b70cbc4 100644 --- a/tools/bindings.cpp +++ b/tools/bindings.cpp @@ -22,7 +22,7 @@ class InterpreterWrapper { const std::string &stdlib = "", const std::string &triggeringCondition = "", uint64_t randomSeed = 42) { - Autumn::SExpParser parser(script); + Autumn::SExpParser parser(script, interpreter->getInterner()); std::vector> stmts = parser.parseStmt(); try { interpreter->start(stmts, stdlib, triggeringCondition, randomSeed); @@ -42,11 +42,11 @@ class InterpreterWrapper { bool getTriggerState() { return interpreter->getTriggerState(); } - void click(int x, int y) { interpreter->getState()->click(x, y); } - void left() { interpreter->getState()->pushLeft(); } - void right() { interpreter->getState()->pushRight(); } - void up() { interpreter->getState()->pushUp(); } - void down() { interpreter->getState()->pushDown(); } + void click(int x, int y) { interpreter->getState().click(x, y); } + void left() { interpreter->getState().pushLeft(); } + void right() { interpreter->getState().pushRight(); } + void up() { interpreter->getState().pushUp(); } + void down() { interpreter->getState().pushDown(); } std::string renderAll() { return interpreter->renderAll(); } void setVerbose(bool verbose) { interpreter->setVerbose(verbose); } diff --git a/tools/bindings_julia.cpp b/tools/bindings_julia.cpp index 356d97f..5dee7f2 100644 --- a/tools/bindings_julia.cpp +++ b/tools/bindings_julia.cpp @@ -18,7 +18,7 @@ class InterpreterWrapper { const std::string &stdlib = "", const std::string &triggeringCondition = "", uint64_t randomSeed = 42) { - Autumn::SExpParser parser(script); + Autumn::SExpParser parser(script, interpreter->getInterner()); std::vector> stmts = parser.parseStmt(); try { interpreter->start(stmts, stdlib, triggeringCondition, randomSeed); @@ -30,7 +30,7 @@ class InterpreterWrapper { std::string tmpExecuteStmt(const std::string &script) { std::shared_ptr sexp = sexpresso::parse(script); - Autumn::SExpParser parser(script); + Autumn::SExpParser parser(script, interpreter->getInterner()); std::shared_ptr stmt = parser.parseStmt(sexp, -1); try { interpreter->tmpExecuteStmt(stmt); @@ -45,7 +45,7 @@ class InterpreterWrapper { } void restoreEnvironment(const std::string &script, const std::string &stdlib, const std::string &triggeringCondition) { - Autumn::SExpParser parser(script); + Autumn::SExpParser parser(script, interpreter->getInterner()); std::vector> stmts = parser.parseStmt(); try { interpreter->start(stmts, stdlib, triggeringCondition); diff --git a/tools/bindings_python.cpp b/tools/bindings_python.cpp index a31f90e..997ff38 100644 --- a/tools/bindings_python.cpp +++ b/tools/bindings_python.cpp @@ -1,33 +1,30 @@ -#include "AutumnValue.hpp" #include "Interpreter.hpp" #include "Parser.hpp" +#include "Stmt.hpp" +#include "Expr.hpp" #include #include #include +#include namespace py = pybind11; // InterpreterWrapper class as provided class InterpreterWrapper { public: - std::shared_ptr interpreter; - - InterpreterWrapper() { - interpreter = std::make_shared(); - } + Autumn::Interpreter interpreter; - - std::string getBackground() { return interpreter->getBackground(); } - int getFrameRate() { return interpreter->getFrameRate(); } + std::string getBackground() { return interpreter.getBackground(); } + int getFrameRate() { return interpreter.getFrameRate(); } std::string runScript(const std::string &script, const std::string &stdlib = "", const std::string &triggeringCondition = "", uint64_t randomSeed = 42) { - Autumn::SExpParser parser(script); + Autumn::SExpParser parser(script, interpreter.getInterner()); std::vector> stmts = parser.parseStmt(); try { - interpreter->start(stmts, stdlib, triggeringCondition, randomSeed); + interpreter.start(stmts, stdlib, triggeringCondition, randomSeed); } catch (const std::runtime_error &e) { return std::string("Runtime Error: ") + e.what(); } @@ -36,26 +33,26 @@ class InterpreterWrapper { std::string tmpExecuteStmt(const std::string &script) { std::shared_ptr sexp = sexpresso::parse(script); - Autumn::SExpParser parser(script); + Autumn::SExpParser parser(script, interpreter.getInterner()); std::shared_ptr stmt = parser.parseStmt(sexp, -1); try { - interpreter->tmpExecuteStmt(stmt); - return interpreter->getEnvironmentString(); + interpreter.tmpExecuteStmt(stmt); + return interpreter.getEnvironmentString(); } catch (const std::runtime_error &e) { return std::string("Runtime Error: ") + e.what(); } } std::string getEnvironmentString() { - return interpreter->getEnvironmentString(); + return interpreter.getEnvironmentString(); } - void restoreEnvironment(const std::string &script, const std::string &stdlib, const std::string &triggeringCondition) { - Autumn::SExpParser parser(script); + void restoreEnvironment(const std::string &script, const std::string &stdlib, const std::string &triggeringCondition) { + Autumn::SExpParser parser(script, interpreter.getInterner()); std::vector> stmts = parser.parseStmt(); try { - interpreter->start(stmts, stdlib, triggeringCondition); - interpreter->restoreEnvironment(); + interpreter.start(stmts, stdlib, triggeringCondition); + interpreter.restoreEnvironment(); } catch (const std::runtime_error &e) { throw std::runtime_error("Runtime Error: " + std::string(e.what())); } @@ -63,28 +60,88 @@ class InterpreterWrapper { // Example method to execute a single expression std::string step() { - interpreter->step(); + interpreter.step(); return "Step executed."; } - void click(int x, int y) { interpreter->getState()->click(x, y); } - void left() { interpreter->getState()->pushLeft(); } - void right() { interpreter->getState()->pushRight(); } - void up() { interpreter->getState()->pushUp(); } - void down() { interpreter->getState()->pushDown(); } + void click(int x, int y) { interpreter.getState().click(x, y); } + void left() { interpreter.getState().pushLeft(); } + void right() { interpreter.getState().pushRight(); } + void up() { interpreter.getState().pushUp(); } + void down() { interpreter.getState().pushDown(); } + + bool getTriggerState() { return interpreter.getTriggerState(); } - bool getTriggerState() { return interpreter->getTriggerState(); } + std::string renderAll() { return interpreter.renderAll(); } + void setVerbose(bool verbose) { interpreter.setVerbose(verbose); } + bool getVerbose() { return interpreter.getVerbose(); } - std::string renderAll() { return interpreter->renderAll(); } - void setVerbose(bool verbose) { interpreter->setVerbose(verbose); } - bool getVerbose() { return interpreter->getVerbose(); } + void setRandomSeed(uint64_t seed) { interpreter.setRandomSeed(seed); } + uint64_t getRandomSeed() { return interpreter.getRandomGenerator()->getSeed(); } - void setRandomSeed(uint64_t seed) { interpreter->setRandomSeed(seed); } - uint64_t getRandomSeed() { return interpreter->getRandomGenerator()->getSeed(); } + std::string evaluateToString(std::string expr) { return interpreter.evaluateToString(expr); } + int getOnClauseCount() { return interpreter.getOnClauseCount(); } + int getCoveredOnClauseCount() { return interpreter.getCoveredOnClauseCount(); } - std::string evaluateToString(std::string expr) { return interpreter->evaluateToString(expr); } - int getOnClauseCount() { return interpreter->getOnClauseCount(); } - int getCoveredOnClauseCount() { return interpreter->getCoveredOnClauseCount(); } + std::vector getCoveredOnClauseIndices() { + const auto &covered = interpreter.getCoveredOnClauseIndices(); + return std::vector(covered.begin(), covered.end()); + } + + // Helper to convert type expression to string + std::string typeExprToString(std::shared_ptr typeexpr) { + auto typeVar = std::dynamic_pointer_cast(typeexpr); + if (typeVar != nullptr) { + return typeVar->name.lexeme; + } + auto listType = std::dynamic_pointer_cast(typeexpr); + if (listType != nullptr) { + return "List[" + typeExprToString(listType->typeexpr) + "]"; + } + return "Unknown"; + } + + // Extract types using C++ parser + py::dict extractTypes(const std::string &script) { + Autumn::SExpParser parser(script, interpreter.getInterner()); + std::vector> stmts = parser.parseStmt(); + + py::dict types; + py::dict globalVars; + + for (const auto &stmt : stmts) { + // Check for Object statement + auto objStmt = std::dynamic_pointer_cast(stmt); + if (objStmt != nullptr) { + py::dict fields; + // Extract fields (TypeDecl expressions) + for (const auto &field : objStmt->fields) { + auto typeDecl = std::dynamic_pointer_cast(field); + if (typeDecl != nullptr) { + fields[typeDecl->name.lexeme.c_str()] = typeExprToString(typeDecl->typeexpr); + } + } + // All objects have origin + fields["origin"] = "Position"; + types[objStmt->name.lexeme.c_str()] = fields; + } + + // Check for TypeDecl in Expression statements (global variable declarations) + auto exprStmt = std::dynamic_pointer_cast(stmt); + if (exprStmt != nullptr) { + auto typeDecl = std::dynamic_pointer_cast(exprStmt->expression); + if (typeDecl != nullptr) { + globalVars[typeDecl->name.lexeme.c_str()] = typeExprToString(typeDecl->typeexpr); + } + } + } + + // Return as dict with types and global_vars + py::dict result; + result["types"] = types; + result["global_vars"] = globalVars; + return result; + } }; PYBIND11_MODULE(interpreter_module, m) { @@ -105,6 +162,7 @@ PYBIND11_MODULE(interpreter_module, m) { .def("get_environment_string", &InterpreterWrapper::getEnvironmentString, "Get environment string") .def("get_on_clause_count", &InterpreterWrapper::getOnClauseCount, "Get on clause count") .def("get_covered_on_clause_count", &InterpreterWrapper::getCoveredOnClauseCount, "Get covered on clause count") + .def("get_covered_on_clause_indices", &InterpreterWrapper::getCoveredOnClauseIndices, "Get list of covered on clause indices") .def("evaluate_to_string", &InterpreterWrapper::evaluateToString, "Evaluate to string") .def("restore_environment", &InterpreterWrapper::restoreEnvironment, "Restore environment") .def("tmp_execute_stmt", &InterpreterWrapper::tmpExecuteStmt, "Execute a statement") @@ -113,7 +171,8 @@ PYBIND11_MODULE(interpreter_module, m) { .def("set_random_seed", &InterpreterWrapper::setRandomSeed, "Set random seed") .def("get_random_seed", &InterpreterWrapper::getRandomSeed, "Get random seed") .def("get_background", &InterpreterWrapper::getBackground, "Get background") - .def("get_frame_rate", &InterpreterWrapper::getFrameRate, "Get frame rate"); + .def("get_frame_rate", &InterpreterWrapper::getFrameRate, "Get frame rate") + .def("extract_types", &InterpreterWrapper::extractTypes, "Extract types from script"); cls.def("run_script", &InterpreterWrapper::runScript, "Run a script", py::arg("script"), py::arg("stdlib"), py::arg("triggeringCondition"), py::arg("randomSeed") = 42); } diff --git a/tools/generate_ast.cpp b/tools/generate_ast.cpp index 06c1231..918ed57 100644 --- a/tools/generate_ast.cpp +++ b/tools/generate_ast.cpp @@ -92,8 +92,7 @@ void defineAst(const std::string &outputDir, const std::string &baseName, className = capitalize(className); - writer << "class " << className << " : public " << baseName - << ", public std::enable_shared_from_this<" + className + "> {\n"; + writer << "class " << className << " : public " << baseName << " {\n"; writer << "public:\n"; // Constructor @@ -134,7 +133,7 @@ void defineAst(const std::string &outputDir, const std::string &baseName, // Override accept method writer << " std::any accept(Visitor& visitor) override {\n"; writer << " return visitor.visit" << className << baseName - << "(shared_from_this());\n"; + << "(*this);\n"; writer << " }\n\n"; // Fields diff --git a/tools/main.cpp b/tools/main.cpp index 896a23d..13b781d 100644 --- a/tools/main.cpp +++ b/tools/main.cpp @@ -1,4 +1,3 @@ -#include "AstPrinter.hpp" #include "AutumnClass.hpp" #include "Interpreter.hpp" #include "Parser.hpp" @@ -48,13 +47,9 @@ int main(int argc, char **argv) { auto sexp = sexpresso::parse(getRenderedAnts); Autumn::SExpParser renderParser(getRenderedAnts); std::shared_ptr renderExpr = renderParser.parseExpr(sexp); - Autumn::AstPrinter printer; - std::cerr << "Render expr: " - << std::any_cast(renderExpr->accept(printer)) - << std::endl; + std::cerr << "Render expr: " << renderExpr->prettyPrint() << std::endl; std::shared_ptr renderedAnts = - std::any_cast>( - renderExpr->accept(interpreter)); + renderExpr->eval(interpreter); std::cout << std::endl << std::endl; std::cout << "Rendered ants: " << std::endl; diff --git a/tools/parser.cpp b/tools/parser.cpp index fa27004..fea7697 100644 --- a/tools/parser.cpp +++ b/tools/parser.cpp @@ -1,6 +1,5 @@ #include "sexpresso.hpp" // adding file handling -#include "AstPrinter.hpp" #include "Parser.hpp" #include #include @@ -19,11 +18,10 @@ std::string readFile(const std::string &filename) { int main(int argc, char **argv) { std::string mysexpr = readFile(argv[1]); Autumn::SExpParser parser(mysexpr); - Autumn::AstPrinter printer; try { std::vector> stmts = parser.parseStmt(); for (const auto &stmt : stmts) { - std::cout << printer.print(stmt) << std::endl; + std::cout << stmt->prettyPrint() << std::endl; } } catch (const std::runtime_error &e) { std::cerr << e.what() << std::endl;