├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitlab-ci.yml ├── .gitmodules ├── .pydevproject ├── .pylintrc ├── AUTHORS ├── Examples ├── LICENSE ├── Makefile ├── README.md ├── Smalltalk ├── TestSuite ├── rebench.conf ├── som.sh ├── src ├── main.py ├── main_basic.py ├── main_rpython.py ├── rlib │ ├── __init__.py │ ├── arithmetic.py │ ├── debug.py │ ├── erased.py │ ├── exit.py │ ├── float.py │ ├── jit.py │ ├── llop.py │ ├── min_heap_queue.py │ ├── objectmodel.py │ ├── osext.py │ ├── rgc.py │ ├── streamio.py │ ├── string_stream.py │ └── unroll.py ├── rtruffle │ ├── __init__.py │ ├── abstract_node.py │ ├── base_node_2.py │ ├── base_node_3.py │ ├── node.py │ └── source_section.py └── som │ ├── __init__.py │ ├── compiler │ ├── __init__.py │ ├── ast │ │ ├── __init__.py │ │ ├── disassembler.py │ │ ├── method_generation_context.py │ │ ├── parser.py │ │ └── variable.py │ ├── bc │ │ ├── __init__.py │ │ ├── bytecode_generator.py │ │ ├── disassembler.py │ │ ├── method_generation_context.py │ │ └── parser.py │ ├── class_generation_context.py │ ├── disassembler.py │ ├── lexer.py │ ├── lexical_scope.py │ ├── method_generation_context.py │ ├── parse_error.py │ ├── parser.py │ ├── sourcecode_compiler.py │ └── symbol.py │ ├── interp_type.py │ ├── interpreter │ ├── __init__.py │ ├── ast │ │ ├── __init__.py │ │ ├── frame.py │ │ └── nodes │ │ │ ├── __init__.py │ │ │ ├── block_node.py │ │ │ ├── contextual_node.py │ │ │ ├── dispatch.py │ │ │ ├── expression_node.py │ │ │ ├── field_node.py │ │ │ ├── global_read_node.py │ │ │ ├── literal_node.py │ │ │ ├── message │ │ │ ├── __init__.py │ │ │ ├── abstract_node.py │ │ │ ├── generic_node.py │ │ │ ├── super_node.py │ │ │ └── uninitialized_node.py │ │ │ ├── return_non_local_node.py │ │ │ ├── sequence_node.py │ │ │ ├── specialized │ │ │ ├── __init__.py │ │ │ ├── down_to_do_node.py │ │ │ ├── if_true_false.py │ │ │ ├── int_inc_node.py │ │ │ ├── literal_and_or.py │ │ │ ├── literal_if.py │ │ │ ├── literal_while.py │ │ │ ├── to_by_do_node.py │ │ │ └── to_do_node.py │ │ │ └── variable_node.py │ ├── bc │ │ ├── __init__.py │ │ ├── bytecodes.py │ │ ├── frame.py │ │ └── interpreter.py │ ├── control_flow.py │ ├── objectstorage │ │ ├── __init__.py │ │ ├── layout_transitions.py │ │ ├── object_layout.py │ │ └── storage_location.py │ └── send.py │ ├── primitives │ ├── __init__.py │ ├── array_primitives.py │ ├── ast │ │ ├── __init__.py │ │ ├── array_primitives.py │ │ ├── block_primitives.py │ │ ├── class_primitives.py │ │ ├── double_primitives.py │ │ ├── false_primitives.py │ │ ├── integer_primitives.py │ │ ├── method_primitives.py │ │ ├── object_primitives.py │ │ ├── primitive_primitives.py │ │ ├── string_primitives.py │ │ ├── symbol_primitives.py │ │ ├── system_primitives.py │ │ └── true_primitives.py │ ├── bc │ │ ├── __init__.py │ │ ├── array_primitives.py │ │ ├── block_primitives.py │ │ ├── class_primitives.py │ │ ├── double_primitives.py │ │ ├── false_primitives.py │ │ ├── integer_primitives.py │ │ ├── method_primitives.py │ │ ├── object_primitives.py │ │ ├── primitive_primitives.py │ │ ├── string_primitives.py │ │ ├── symbol_primitives.py │ │ ├── system_primitives.py │ │ └── true_primitives.py │ ├── block_primitives.py │ ├── class_primitives.py │ ├── double_primitives.py │ ├── false_primitives.py │ ├── integer_primitives.py │ ├── invokable_primitives.py │ ├── known.py │ ├── object_primitives.py │ ├── primitives.py │ ├── string_primitives.py │ ├── symbol_primitives.py │ ├── system_primitives.py │ └── true_primitives.py │ ├── vm │ ├── __init__.py │ ├── current.py │ ├── globals.py │ ├── shell.py │ ├── symbols.py │ └── universe.py │ └── vmobjects │ ├── __init__.py │ ├── abstract_object.py │ ├── array.py │ ├── biginteger.py │ ├── block_ast.py │ ├── block_bc.py │ ├── clazz.py │ ├── double.py │ ├── integer.py │ ├── method.py │ ├── method_ast.py │ ├── method_bc.py │ ├── method_trivial.py │ ├── object_with_layout.py │ ├── object_without_fields.py │ ├── primitive.py │ ├── string.py │ └── symbol.py ├── tests ├── jit.py ├── rtruffle_tests │ ├── __init__.py │ └── test_node.py ├── test_array.py ├── test_ast_inlining.py ├── test_basic_interpreter.py ├── test_bc_frame.py ├── test_bytecode_generation.py ├── test_optimize_trivial.py ├── test_printable_locations.py └── test_som.py ├── trace-filter.py └── translate.sh /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test_som: 7 | runs-on: ubuntu-24.04 # ubuntu-latest 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | include: 12 | - name: AST Interpreter PyPy2 13 | id: ast 14 | interp: AST 15 | python-version: pypy2.7 16 | - name: Bytecode Interpreter PyPy2 17 | id: bc 18 | interp: BC 19 | python-version: pypy2.7 20 | - name: Basics PyPy 2.7 21 | python-version: pypy2.7 22 | id: basic 23 | - name: Basics Python 3.13 24 | python-version: 3.13 25 | id: basic 26 | 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v4 30 | with: 31 | submodules: true 32 | 33 | - name: Set up PyPy 34 | uses: actions/setup-python@v5 35 | with: 36 | python-version: ${{ matrix.python-version }} 37 | 38 | - name: Download PyPy Sources 39 | if: matrix.id != 'basic' 40 | run: | 41 | export PYPYVER=v7.3.18 42 | curl https://downloads.python.org/pypy/pypy2.7-${PYPYVER}-src.tar.bz2 -o pypy.tar.bz2 43 | tar -xjf pypy.tar.bz2 44 | mv pypy2.7-${PYPYVER}-src .pypy 45 | 46 | - name: Install PyTest 47 | run: | 48 | pip install pytest 49 | 50 | - name: Tests 51 | if: matrix.id == 'basic' 52 | run: | 53 | export PYTHON=python 54 | export PYTHONPATH=src 55 | SOM_INTERP=AST pytest 56 | SOM_INTERP=BC pytest 57 | SOM_INTERP=AST ./som.sh -cp Smalltalk TestSuite/TestHarness.som 58 | SOM_INTERP=BC ./som.sh -cp Smalltalk TestSuite/TestHarness.som 59 | echo "[system exit: 0] value" | SOM_INTERP=AST ./som.sh -cp Smalltalk 60 | echo "[system exit: 0] value" | SOM_INTERP=BC ./som.sh -cp Smalltalk 61 | 62 | - name: Full Tests 63 | if: matrix.id != 'basic' 64 | run: | 65 | export SOM_INTERP=${{ matrix.interp }} 66 | PYTHONPATH=$PYTHONPATH:.pypy:src pytest 67 | PYTHONPATH=$PYTHONPATH:.pypy ./som.sh -cp Smalltalk TestSuite/TestHarness.som 68 | PYTHONPATH=$PYTHONPATH:.pypy .pypy/rpython/bin/rpython --batch src/main_rpython.py 69 | ./som-${{ matrix.id }}-interp -cp Smalltalk TestSuite/TestHarness.som 70 | 71 | - name: SomSom Tests 72 | if: matrix.id != 'basic' 73 | run: | 74 | ./som-${{ matrix.id }}-interp -cp core-lib/Smalltalk:core-lib/TestSuite:core-lib/SomSom/src/compiler:core-lib/SomSom/src/vm:core-lib/SomSom/src/vmobjects:core-lib/SomSom/src/interpreter:core-lib/SomSom/src/primitives \ 75 | core-lib/SomSom/tests/SomSomTests.som 76 | 77 | - name: Install and Run Black 78 | run: | 79 | pip install black 80 | black --check --diff src tests 81 | if: matrix.python-version == '3.11' 82 | 83 | - name: Install and Run PyLint 84 | run: | 85 | pip install pylint 86 | pylint --init-hook="import sys; sys.setrecursionlimit(2000)" src tests 87 | if: matrix.python-version == '3.11' 88 | 89 | 90 | concurrency: 91 | group: "workflow = ${{ github.workflow }}, ref = ${{ github.event.ref }}, pr = ${{ github.event.pull_request.id }}" 92 | cancel-in-progress: ${{ github.event_name == 'pull_request' }} 93 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.DS_Store 3 | som-ast* 4 | som-bc* 5 | pypy 6 | .settings 7 | .idea 8 | src/attic 9 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "core-lib"] 2 | path = core-lib 3 | url = https://github.com/SOM-st/SOM.git 4 | -------------------------------------------------------------------------------- /.pydevproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /${PROJECT_DIR_NAME}/src 5 | /${PROJECT_DIR_NAME}/tests 6 | 7 | python 2.7 8 | PyPy 9 | 10 | /Users/smarr/Projects/SOM/PySOM/pypy/pypy 11 | 12 | 13 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | This file lists all people who have contributed to the RPySOM VM. 2 | 3 | SOM was originally implemented at the University of Aarhus (Denmark) in 4 | 2001/2002. The implementation of SOM was done by Jakob Roland Andersen, Kasper 5 | Verdich Lund, Lars Bak, Mads Torgersen, and Ulrik Pagh Schultz. They also 6 | wrote the original versions of the SOM Smalltalk libraries, test suites, and 7 | benchmarks, that are (in extended versions) bundled with SOM. 8 | 9 | SOM was used by Michael Haupt in courses on virtual machines at Lancaster 10 | University and Technische Universitaet Darmstadt (Germany) in VM courses in 11 | 2006. During that time, some changes were applied to SOM by Michael Haupt and 12 | Sebastian Kanthak. 13 | 14 | PySOM is a port by Stefan Marr of SOM's Java implementation. Later it became 15 | RPySOM by porting it to RPython, and then it was changed from using a bytecode 16 | interpreter to use an AST-based interpreter to become RTruffleSOM. 17 | RTruffleSOM contains contributions from Carl Friedrich Bolz and Tobias Pape. 18 | In the now current version, it merges RPySOM and RTruffleSOM to be in a single 19 | repository, but preserving the option to use either AST or bytecode-based 20 | interpretation. 21 | 22 | 2020-09-02, Stefan Marr 23 | 24 | -------------------------------------------------------------------------------- /Examples: -------------------------------------------------------------------------------- 1 | core-lib/Examples -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Stefan Marr, mail@stefan-marr.de 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env make -f 2 | 3 | PYPY_DIR ?= pypy 4 | RPYTHON ?= $(PYPY_DIR)/rpython/bin/rpython 5 | 6 | .PHONY: compile som-interp som-jit som-ast-jit som-bc-jit som-bc-interp som-ast-interp 7 | 8 | all: compile 9 | 10 | compile: som-ast-jit 11 | 12 | som-ast-jit: core-lib/.git 13 | SOM_INTERP=AST PYTHONPATH=$(PYTHONPATH):$(PYPY_DIR) $(RPYTHON) --batch -Ojit src/main_rpython.py 14 | 15 | som-bc-jit: core-lib/.git 16 | SOM_INTERP=BC PYTHONPATH=$(PYTHONPATH):$(PYPY_DIR) $(RPYTHON) --batch -Ojit src/main_rpython.py 17 | 18 | som-ast-interp: core-lib/.git 19 | SOM_INTERP=AST PYTHONPATH=$(PYTHONPATH):$(PYPY_DIR) $(RPYTHON) --batch src/main_rpython.py 20 | 21 | som-bc-interp: core-lib/.git 22 | SOM_INTERP=BC PYTHONPATH=$(PYTHONPATH):$(PYPY_DIR) $(RPYTHON) --batch src/main_rpython.py 23 | 24 | som-interp: som-ast-interp som-bc-interp 25 | 26 | som-jit: som-ast-jit som-bc-jit 27 | 28 | test: compile 29 | PYTHONPATH=$(PYTHONPATH):$(PYPY_DIR) nosetests 30 | if [ -e ./som-ast-jit ]; then ./som-ast-jit -cp Smalltalk TestSuite/TestHarness.som; fi 31 | if [ -e ./som-bc-jit ]; then ./som-bc-jit -cp Smalltalk TestSuite/TestHarness.som; fi 32 | if [ -e ./som-ast-interp ]; then ./som-ast-interp -cp Smalltalk TestSuite/TestHarness.som; fi 33 | if [ -e ./som-bc-interp ]; then ./som-bc-interp -cp Smalltalk TestSuite/TestHarness.som; fi 34 | 35 | clean: 36 | @-rm som-ast-jit som-ast-interp 37 | @-rm som-bc-jit som-bc-interp 38 | 39 | core-lib/.git: 40 | git submodule update --init 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PySOM - The Simple Object Machine Smalltalk 2 | =========================================== 3 | 4 | Introduction 5 | ------------ 6 | 7 | SOM is a minimal Smalltalk dialect that was used to teach at the [Hasso 8 | Plattner Institute][SOM] and before that at the University of Århus 9 | (Denmark) where it was used for teaching and as the foundation for [Resilient 10 | Smalltalk][RS]. 11 | 12 | In addition to PySOM, other implementations exist for Java (SOM, TruffleSOM), 13 | C (CSOM), C++ (SOM++), Python (PySOM), and Squeak/Pharo Smalltalk (AweSOM). 14 | 15 | A simple Hello World looks like: 16 | 17 | ```Smalltalk 18 | Hello = ( 19 | run = ( 20 | 'Hello World!' println. 21 | ) 22 | ) 23 | ``` 24 | 25 | This repository contains a Python-base implementation of SOM, including 26 | SOM's standard library, and a number of benchmarks. The [main project 27 | page][SOMst] has links to other SOM VM implementations. 28 | 29 | PySOM implementation use either an abstract-syntax-tree or a 30 | bytecode-based interpreter. One can choose between them with the `SOM_INTERP` environment variable. 31 | 32 | - AST-based interpreter: `SOM_INTERP=AST` 33 | - bytecode-based interpreter: `SOM_INTERP=BC` 34 | 35 | To check out the code, run: 36 | 37 | git clone --recurse-submodules https://github.com/SOM-st/PySOM.git 38 | 39 | Note the `--recurse-submodules` option. It makes sure that the core library, 40 | i.e., the Smalltalk code is downloaded. 41 | 42 | PySOM's tests can be executed with: 43 | 44 | SOM_INTERP=AST ./som.sh -cp Smalltalk TestSuite/TestHarness.som 45 | 46 | A simple Hello World program can be started with: 47 | 48 | SOM_INTERP=AST ./som.sh -cp Smalltalk Examples/Hello.som 49 | 50 | To compile PySOM, a recent PyPy is recommended and the RPython source 51 | code is required. The source distribution of PyPy 7.3 can be used like this: 52 | 53 | wget https://downloads.python.org/pypy/pypy2.7-v7.3.1-src.tar.bz2 54 | tar xvf pypy2.7-v7.3.1-src.tar.bz2 55 | export PYPY_DIR=`pwd`/pypy2.7-v7.3.1-src/ 56 | 57 | Information on previous authors are included in the AUTHORS file. This code is 58 | distributed under the MIT License. Please see the LICENSE file for details. 59 | 60 | 61 | History 62 | ------- 63 | 64 | In 2013, the implementations of PySOM, RPySOM, and RTruffleSOM where split 65 | over multiple repositories. Since end of 2020, they are reunited here and PySOM 66 | can be used with Python 2.7, Python 3.8, as well as compiled with RPython. 67 | Thus, https://github.com/SOM-st/PySOM is again the only and the canonical 68 | repository. 69 | 70 | 71 | Build Status 72 | ------------ 73 | 74 | Thanks to GitHub Actions, all pull requests of this repository are automatically tested. 75 | The current build status is: [![Build Status](https://github.com/SOM-st/PySOM/actions/workflows/ci.yml/badge.svg)](https://github.com/SOM-st/PySOM/actions) 76 | 77 | [SOM]: http://www.hpi.uni-potsdam.de/hirschfeld/projects/som/ 78 | [SOMst]: https://travis-ci.org/SOM-st/ 79 | [RS]: http://dx.doi.org/10.1016/j.cl.2005.02.003 80 | -------------------------------------------------------------------------------- /Smalltalk: -------------------------------------------------------------------------------- 1 | core-lib/Smalltalk -------------------------------------------------------------------------------- /TestSuite: -------------------------------------------------------------------------------- 1 | core-lib/TestSuite -------------------------------------------------------------------------------- /som.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | DIR="`dirname \"$0\"`" 3 | if [ -z "$PYTHON" ]; then 4 | PYTHON=pypy 5 | fi 6 | if [ -z "$PYPY_DIR" ]; then 7 | PYPY_DIR=$DIR/pypy 8 | fi 9 | export PYTHONPATH=$DIR/src:$PYPY_DIR:$PYTHONPATH 10 | exec $PYTHON $DIR/src/main.py "$@" 11 | -------------------------------------------------------------------------------- /src/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from som.compiler.parse_error import ParseError 3 | from som.vm.universe import main, Exit 4 | 5 | 6 | try: 7 | main(sys.argv) 8 | except Exit as ex: 9 | sys.exit(ex.code) 10 | except ParseError as ex: 11 | from som.vm.universe import error_println 12 | 13 | error_println(str(ex)) 14 | sys.exit(1) 15 | 16 | sys.exit(0) 17 | -------------------------------------------------------------------------------- /src/main_basic.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from som.vm.current import current_universe 5 | 6 | core_lib_path = os.path.dirname(os.path.abspath(__file__)) + "/../core-lib/" 7 | current_universe.setup_classpath( 8 | core_lib_path + "Smalltalk:" + core_lib_path + "TestSuite/BasicInterpreterTests" 9 | ) 10 | result = current_universe.execute_method(sys.argv[1], sys.argv[2]) 11 | print(result) 12 | -------------------------------------------------------------------------------- /src/main_rpython.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import os 5 | import sys 6 | 7 | from som.compiler.parse_error import ParseError 8 | from som.interp_type import is_ast_interpreter, is_bytecode_interpreter 9 | from som.vm.universe import main, Exit 10 | 11 | try: 12 | import rpython.rlib # pylint: disable=unused-import 13 | except ImportError: 14 | "NOT_RPYTHON" 15 | print("Failed to load RPython library. Please make sure it is on PYTHONPATH") 16 | sys.exit(1) 17 | 18 | # __________ Entry points __________ 19 | 20 | 21 | def entry_point(argv): 22 | try: 23 | main(argv) 24 | except Exit as ex: 25 | return ex.code 26 | except ParseError as ex: 27 | os.write(2, str(ex)) 28 | return 1 29 | except Exception as ex: # pylint: disable=broad-except 30 | os.write(2, "ERROR: %s thrown during execution.\n" % ex) 31 | return 1 32 | return 1 33 | 34 | 35 | # _____ Define and setup target ___ 36 | 37 | 38 | def target(driver, _args): 39 | exe_name = "som-" 40 | if is_ast_interpreter(): 41 | exe_name += "ast-" 42 | elif is_bytecode_interpreter(): 43 | exe_name += "bc-" 44 | 45 | if driver.config.translation.jit: 46 | exe_name += "jit" 47 | else: 48 | exe_name += "interp" 49 | 50 | driver.exe_name = exe_name 51 | return entry_point, None 52 | 53 | 54 | def jitpolicy(_driver): 55 | from rpython.jit.codewriter.policy import JitPolicy # pylint: disable=import-error 56 | 57 | return JitPolicy() 58 | 59 | 60 | if __name__ == "__main__": 61 | from rpython.translator.driver import TranslationDriver # pylint: disable=E 62 | 63 | f, _ = target(TranslationDriver(), sys.argv) 64 | sys.exit(f(sys.argv)) 65 | -------------------------------------------------------------------------------- /src/rlib/__init__.py: -------------------------------------------------------------------------------- 1 | """Compatibility library for RPython 2 | 3 | This module contains ad hoc implementations and workarounds for Python 4 | library functions that is not directly supported by RPython. 5 | """ 6 | -------------------------------------------------------------------------------- /src/rlib/arithmetic.py: -------------------------------------------------------------------------------- 1 | try: 2 | from rpython.rlib.rarithmetic import ovfcheck, LONG_BIT # pylint: disable=W 3 | from rpython.rlib.rbigint import rbigint, _divrem as divrem # pylint: disable=W 4 | from rpython.rlib.rbigint import rbigint as BigIntType # pylint: disable=W 5 | from rpython.rlib.rarithmetic import string_to_int # pylint: disable=unused-import 6 | from rpython.rlib.rstring import ParseStringOverflowError # pylint: disable=W 7 | 8 | bigint_from_int = rbigint.fromint 9 | bigint_from_str = rbigint.fromstr 10 | IntType = int 11 | except ImportError: 12 | "NOT_RPYTHON" 13 | 14 | def ovfcheck(value): 15 | return value 16 | 17 | def bigint_from_int(value): 18 | return value 19 | 20 | def bigint_from_str(value): 21 | return int(value) 22 | 23 | def divrem(x, y): 24 | raise Exception("not yet implemented") 25 | 26 | string_to_int = int # pylint: disable=invalid-name 27 | 28 | class ParseStringOverflowError(Exception): 29 | def __init__(self, parser): # pylint: disable=super-init-not-called 30 | self.parser = parser 31 | 32 | LONG_BIT = 0x8000000000000000 33 | 34 | import sys 35 | 36 | if sys.version_info.major <= 2: 37 | IntType = (int, long) # pylint: disable=undefined-variable 38 | BigIntType = long # pylint: disable=undefined-variable 39 | else: 40 | IntType = int 41 | BigIntType = int 42 | -------------------------------------------------------------------------------- /src/rlib/debug.py: -------------------------------------------------------------------------------- 1 | try: 2 | from rpython.rlib.debug import make_sure_not_resized # pylint: disable=W 3 | except ImportError: 4 | "NOT_RPYTHON" 5 | 6 | def make_sure_not_resized(_): 7 | pass 8 | -------------------------------------------------------------------------------- /src/rlib/erased.py: -------------------------------------------------------------------------------- 1 | try: 2 | from rpython.rlib.rerased import new_erasing_pair # pylint: disable=unused-import 3 | from rpython.rlib.rerased import erase_int # pylint: disable=unused-import 4 | from rpython.rlib.rerased import unerase_int # pylint: disable=unused-import 5 | except ImportError: 6 | "NOT_RPYTHON" 7 | 8 | def new_erasing_pair(name): 9 | identity = _ErasingPairIdentity(name) 10 | 11 | def erase(x): 12 | return _Erased(x, identity) 13 | 14 | def unerase(y): 15 | assert y._identity is identity # pylint: disable=W 16 | return y._x # pylint: disable=W 17 | 18 | return erase, unerase 19 | 20 | def erase_int(val): 21 | return val 22 | 23 | def unerase_int(val): 24 | return val 25 | 26 | class _ErasingPairIdentity(object): 27 | def __init__(self, name): 28 | self.name = name 29 | 30 | class _Erased(object): 31 | def __init__(self, x, identity): 32 | self._x = x 33 | self._identity = identity 34 | 35 | def __str__(self): 36 | return "Erased(" + str(self._x) + ", " + self._identity.name + ")" 37 | -------------------------------------------------------------------------------- /src/rlib/exit.py: -------------------------------------------------------------------------------- 1 | class Exit(Exception): 2 | """ 3 | Use an exit exception to end program execution. 4 | We don't use sys.exit because it is a little problematic with RPython. 5 | """ 6 | 7 | def __init__(self, code): # pylint: disable=super-init-not-called 8 | self.code = code 9 | -------------------------------------------------------------------------------- /src/rlib/float.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | INFINITY = 1e200 * 1e200 4 | 5 | try: 6 | from rpython.rlib.rfloat import formatd, DTSF_ADD_DOT_0, DTSF_STR_PRECISION 7 | from rpython.rlib.rfloat import round_double # pylint: disable=unused-import 8 | 9 | def float_to_str(value): 10 | return formatd(value, "g", DTSF_STR_PRECISION, DTSF_ADD_DOT_0) 11 | 12 | except ImportError: 13 | "NOT_RPYTHON" 14 | 15 | def float_to_str(value): 16 | return str(value) 17 | 18 | def round_double(value, _ndigits): 19 | # round() from libm, which is not available on all platforms! 20 | # This version rounds away from zero. 21 | abs_value = abs(value) 22 | rounded = math.floor(abs_value + 0.5) 23 | if rounded - abs_value < 1.0: 24 | return math.copysign(rounded, value) 25 | 26 | # 'abs_value' is just in the wrong range: its exponent is precisely 27 | # the one for which all integers are representable but not any 28 | # half-integer. It means that 'abs_value + 0.5' computes equal to 29 | # 'abs_value + 1.0', which is not equal to 'abs_value'. So 'rounded - abs_value' 30 | # computes equal to 1.0. In this situation, we can't return 31 | # 'rounded' because 'abs_value' was already an integer but 'rounded' is the next 32 | # integer! But just returning the original 'x' is fine. 33 | return value 34 | -------------------------------------------------------------------------------- /src/rlib/jit.py: -------------------------------------------------------------------------------- 1 | try: 2 | from rpython.rlib.jit import elidable # pylint: disable=unused-import 3 | from rpython.rlib.jit import elidable_promote # pylint: disable=unused-import 4 | from rpython.rlib.jit import promote # pylint: disable=unused-import 5 | from rpython.rlib.jit import unroll_safe # pylint: disable=unused-import 6 | from rpython.rlib.jit import JitDriver # pylint: disable=unused-import 7 | from rpython.rlib.jit import set_param # pylint: disable=unused-import 8 | from rpython.rlib.jit import dont_look_inside # pylint: disable=unused-import 9 | from rpython.rlib.jit import we_are_jitted # pylint: disable=unused-import 10 | from rpython.rlib.jit import hint # pylint: disable=unused-import 11 | except ImportError: 12 | "NOT_RPYTHON" 13 | 14 | def elidable(func): 15 | return func 16 | 17 | def elidable_promote(_promote_args="all"): 18 | def decorator(func): 19 | return func 20 | 21 | return decorator 22 | 23 | def promote(value): 24 | return value 25 | 26 | def unroll_safe(func): 27 | return func 28 | 29 | def dont_look_inside(func): 30 | return func 31 | 32 | class JitDriver(object): 33 | def __init__( 34 | self, 35 | greens=None, 36 | reds=None, 37 | virtualizables=None, 38 | get_jitcell_at=None, 39 | set_jitcell_at=None, 40 | get_printable_location=None, 41 | confirm_enter_jit=None, 42 | can_never_inline=None, 43 | should_unroll_one_iteration=None, 44 | name="jitdriver", 45 | check_untranslated=True, 46 | vectorize=False, 47 | get_unique_id=None, 48 | is_recursive=False, 49 | get_location=None, 50 | ): 51 | pass 52 | 53 | def jit_merge_point(_self, **_live_vars): # pylint: disable=no-self-argument 54 | pass 55 | 56 | def can_enter_jit(_self, **_live_vars): # pylint: disable=no-self-argument 57 | pass 58 | 59 | def set_param(_driver, _name, _value): 60 | pass 61 | 62 | def we_are_jitted(): 63 | pass 64 | 65 | def hint(x, **_kwds): 66 | return x 67 | -------------------------------------------------------------------------------- /src/rlib/llop.py: -------------------------------------------------------------------------------- 1 | try: 2 | from rpython.rtyper.lltypesystem.lloperation import llop 3 | from rpython.rtyper.lltypesystem.lltype import Signed, Unsigned 4 | from rpython.rtyper.lltypesystem.rffi import cast, UINT, INT 5 | 6 | def as_32_bit_unsigned_value(int_value): 7 | return cast(Signed, cast(UINT, int_value)) 8 | 9 | def as_32_bit_signed_value(int_value): 10 | return cast(Signed, cast(INT, int_value)) 11 | 12 | def unsigned_right_shift(left_val, right_val): 13 | u_l = cast(Unsigned, left_val) 14 | u_r = cast(Unsigned, right_val) 15 | 16 | return cast(Signed, u_l >> u_r) 17 | 18 | int_mod = llop.int_mod 19 | 20 | except ImportError: 21 | "NOT_RPYTHON" 22 | 23 | def int_mod(_type, left, right): 24 | if left > 0: 25 | return abs(left) % abs(right) 26 | return 0 - (abs(left) % abs(right)) 27 | 28 | def as_32_bit_unsigned_value(int_value): 29 | return int_value & 0xFFFFFFFF 30 | 31 | def as_32_bit_signed_value(int_value): 32 | is_negative = (int_value & 0x80000000) == 0x80000000 33 | value = int_value & 0x7FFFFFFF 34 | if is_negative: 35 | value = 0 - (0x80000000 - value) 36 | return value 37 | 38 | def unsigned_right_shift(left_val, right_val): 39 | return left_val >> right_val 40 | 41 | Signed = 1 42 | Unsigned = 2 43 | -------------------------------------------------------------------------------- /src/rlib/min_heap_queue.py: -------------------------------------------------------------------------------- 1 | # Verbatim copy basic heapq operations, only change is inlining < 2 | # This is the version of Python 2.7 3 | # https://github.com/python/cpython/blob/8d21aa21f2cbc6d50aab3f420bb23be1d081dac4/Lib/heapq.py 4 | # For the license of this file, see Python License v2 5 | # https://github.com/python/cpython/blob/main/LICENSE 6 | 7 | 8 | class HeapEntry(object): 9 | def __init__(self, address): 10 | self.address = address 11 | 12 | 13 | def heappush(heap, item): 14 | """Push item onto heap, maintaining the heap invariant.""" 15 | heap.append(item) 16 | _siftdown(heap, 0, len(heap) - 1) 17 | 18 | 19 | def heappop(heap): 20 | """Pop the smallest item off the heap, maintaining the heap invariant.""" 21 | lastelt = heap.pop() # raises appropriate IndexError if heap is empty 22 | if heap: 23 | returnitem = heap[0] 24 | heap[0] = lastelt 25 | _siftup(heap, 0) 26 | else: 27 | returnitem = lastelt 28 | return returnitem 29 | 30 | 31 | def _siftdown(heap, startpos, pos): 32 | newitem = heap[pos] 33 | # Follow the path to the root, moving parents down until finding a place 34 | # newitem fits. 35 | while pos > startpos: 36 | parentpos = (pos - 1) >> 1 37 | parent = heap[parentpos] 38 | if newitem.address < parent.address: 39 | heap[pos] = parent 40 | pos = parentpos 41 | continue 42 | break 43 | heap[pos] = newitem 44 | 45 | 46 | def _siftup(heap, pos): 47 | endpos = len(heap) 48 | startpos = pos 49 | newitem = heap[pos] 50 | # Bubble up the smaller child until hitting a leaf. 51 | childpos = 2 * pos + 1 # leftmost child position 52 | while childpos < endpos: 53 | # Set childpos to index of smaller child. 54 | rightpos = childpos + 1 55 | if rightpos < endpos and not heap[childpos].address < heap[rightpos].address: 56 | childpos = rightpos 57 | # Move the smaller child up. 58 | heap[pos] = heap[childpos] 59 | pos = childpos 60 | childpos = 2 * pos + 1 61 | # The leaf at pos is empty now. Put newitem there, and bubble it up 62 | # to its final resting place (by sifting its parents down). 63 | heap[pos] = newitem 64 | _siftdown(heap, startpos, pos) 65 | -------------------------------------------------------------------------------- /src/rlib/objectmodel.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | if sys.version_info.major > 2: 4 | StrType = str 5 | else: 6 | StrType = (str, unicode) # pylint: disable=undefined-variable 7 | 8 | try: 9 | from rpython.rlib.objectmodel import we_are_translated # pylint: disable=W 10 | from rpython.rlib.objectmodel import compute_identity_hash # pylint: disable=W 11 | from rpython.rlib.objectmodel import compute_hash # pylint: disable=unused-import 12 | from rpython.rlib.longlong2float import longlong2float # pylint: disable=W 13 | from rpython.rlib.longlong2float import float2longlong # pylint: disable=W 14 | except ImportError: 15 | "NOT_RPYTHON" 16 | 17 | def we_are_translated(): 18 | return False 19 | 20 | def compute_identity_hash(x): 21 | assert x is not None 22 | return object.__hash__(x) 23 | 24 | def compute_hash(x): 25 | if isinstance(x, StrType): 26 | return hash(x) 27 | if isinstance(x, int): 28 | return x 29 | if isinstance(x, float): 30 | return hash(x) 31 | if isinstance(x, tuple): 32 | return hash(x) 33 | if x is None: 34 | return 0 35 | return compute_identity_hash(x) 36 | 37 | def longlong2float(value): 38 | return value 39 | 40 | def float2longlong(value): 41 | return value 42 | -------------------------------------------------------------------------------- /src/rlib/osext.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from rlib.string_stream import decode_str 4 | 5 | 6 | def path_split(path): 7 | """ 8 | This is a replacement for the combined use of os.path.split and 9 | os.path.splitext to decompose a relative path into its components. 10 | """ 11 | path_and_file = path.rsplit(os.sep, 1) 12 | if len(path_and_file) <= 1: 13 | path = "" 14 | else: 15 | path = path_and_file[0] 16 | file_and_ext = path_and_file[-1].rsplit(".", 1) 17 | if len(file_and_ext) <= 1: 18 | ext = "" 19 | else: 20 | ext = file_and_ext[-1] 21 | file_name = file_and_ext[0] 22 | return path, file_name, ext 23 | 24 | 25 | def _read_raw(answer): 26 | buf = os.read(0, 32) 27 | if len(buf) == 0: 28 | return answer, False 29 | if buf[-1] == b"\n"[0]: 30 | return answer + decode_str(buf[:-1]), False 31 | return answer + decode_str(buf), True 32 | 33 | 34 | def raw_input(msg=b""): 35 | os.write(1, msg) 36 | answer, cont = _read_raw("") 37 | while cont: 38 | answer, cont = _read_raw(answer) 39 | return answer 40 | -------------------------------------------------------------------------------- /src/rlib/rgc.py: -------------------------------------------------------------------------------- 1 | try: 2 | from rpython.rlib.rgc import collect # pylint: disable=unused-import 3 | from rpython.rlib.rgc import disable # pylint: disable=unused-import 4 | from rpython.rlib.rgc import isenabled # pylint: disable=unused-import 5 | except ImportError: 6 | "NOT_RPYTHON" 7 | 8 | def collect(): 9 | pass 10 | 11 | def disable(): 12 | pass 13 | 14 | def isenabled(): 15 | return 1 16 | -------------------------------------------------------------------------------- /src/rlib/streamio.py: -------------------------------------------------------------------------------- 1 | try: 2 | from rpython.rlib.streamio import open_file_as_stream # pylint: disable=W 3 | except ImportError: 4 | "NOT_RPYTHON" 5 | 6 | def open_file_as_stream(file_name, mode): 7 | return open(file_name, mode) # pylint: disable=unspecified-encoding 8 | 9 | 10 | # Taken from PyPy rpython/rlib/streamio.io (Version 7.3.1) 11 | def readall_from_stream(stream): 12 | bufsize = 8192 13 | result = [] 14 | while True: 15 | try: 16 | data = stream.read(bufsize) 17 | except OSError: 18 | # like CPython < 3.4, partial results followed by an error 19 | # are returned as data 20 | if not result: 21 | raise 22 | break 23 | if not data: 24 | break 25 | result.append(data) 26 | if bufsize < 4194304: # 4 Megs 27 | bufsize <<= 1 28 | return "".join(result) 29 | -------------------------------------------------------------------------------- /src/rlib/string_stream.py: -------------------------------------------------------------------------------- 1 | try: 2 | from rpython.rlib.streamio import Stream, StreamError 3 | 4 | def encode_to_bytes(str_value): 5 | return str_value 6 | 7 | def decode_str(str_value): 8 | return str_value 9 | 10 | except ImportError: 11 | "NOT_RPYTHON" 12 | 13 | class Stream(object): 14 | pass 15 | 16 | class StreamError(Exception): 17 | pass 18 | 19 | import sys 20 | 21 | if sys.version_info.major > 2: 22 | 23 | def encode_to_bytes(str_value): 24 | return str_value.encode("utf-8") 25 | 26 | def decode_str(str_value): 27 | return str_value.decode("utf-8") 28 | 29 | else: 30 | 31 | def encode_to_bytes(str_value): 32 | return str_value 33 | 34 | def decode_str(str_value): 35 | return str_value 36 | 37 | 38 | class StringStream(Stream): 39 | def __init__(self, string): 40 | self._string = string 41 | self.pos = 0 42 | self.max = len(string) - 1 43 | 44 | def write(self, data): 45 | raise StreamError("StringStream is not writable") 46 | 47 | def truncate(self, size): 48 | raise StreamError("StringStream is immutable") 49 | 50 | def tell(self): 51 | return self.pos 52 | 53 | def seek(self, offset, whence): 54 | if whence == 0: 55 | self.pos = max(0, offset) 56 | elif whence == 1: 57 | self.pos = max(0, self.pos + offset) 58 | elif whence == 2: 59 | self.pos = max(0, self.max + offset) 60 | else: 61 | raise StreamError("seek(): whence must be 0, 1 or 2") 62 | 63 | def read(self, n): 64 | assert isinstance(n, int) 65 | end = self.pos + n 66 | assert end >= 0 67 | data = self._string[self.pos : end] 68 | self.pos += len(data) 69 | return data 70 | 71 | def readline(self): 72 | for i in range(self.pos, len(self._string)): 73 | if self._string[i] == "\n": 74 | return self.read(i - self.pos + 1) 75 | if self.pos == 0: 76 | self.pos = len(self._string) 77 | return self._string 78 | return self.read(len(self._string) - self.pos) 79 | -------------------------------------------------------------------------------- /src/rlib/unroll.py: -------------------------------------------------------------------------------- 1 | try: 2 | from rpython.rlib.unroll import unrolling_iterable # pylint: disable=unused-import 3 | except ImportError: 4 | "NOT_RPYTHON" 5 | 6 | def unrolling_iterable(values): 7 | return values 8 | -------------------------------------------------------------------------------- /src/rtruffle/__init__.py: -------------------------------------------------------------------------------- 1 | """A Basic Truffle-like Library 2 | 3 | This library is vaguely inspired by Truffle and provides some support 4 | classes to for AST-like interpreters. 5 | """ 6 | -------------------------------------------------------------------------------- /src/rtruffle/abstract_node.py: -------------------------------------------------------------------------------- 1 | from rlib.unroll import unrolling_iterable 2 | 3 | 4 | class AbstractNode(object): 5 | pass 6 | 7 | 8 | def _get_all_child_fields(clazz): 9 | cls = clazz 10 | field_names = [] 11 | while cls is not AbstractNode: 12 | if hasattr(cls, "_child_nodes_"): 13 | field_names += cls._child_nodes_ # pylint: disable=protected-access 14 | cls = cls.__base__ 15 | 16 | return set(field_names) 17 | 18 | 19 | def _generate_replace_method(cls): 20 | child_fields = unrolling_iterable(_get_all_child_fields(cls)) 21 | 22 | def _replace_child_with(parent_node, old_child, new_child): 23 | was_replaced = False # pylint: disable=unused-variable 24 | for child_slot in child_fields: 25 | if child_slot.endswith("[*]"): 26 | slot_name = child_slot[:-3] 27 | nodes = getattr(parent_node, slot_name) 28 | if nodes and old_child in nodes: 29 | # update old list, because iterators might have a copy of it 30 | for i, n in enumerate(nodes): 31 | if n is old_child: 32 | nodes[i] = new_child 33 | setattr( 34 | parent_node, slot_name, nodes[:] 35 | ) # TODO: figure out whether we need the copy of the list here 36 | was_replaced = True 37 | else: 38 | current = getattr(parent_node, child_slot) 39 | if current is old_child: 40 | setattr(parent_node, child_slot, new_child) 41 | was_replaced = True 42 | # TODO: method recursion is a problem causing specialization more than 43 | # once of a node if the containing method is already on the stack 44 | # if not was_replaced: 45 | # raise ValueError("%s was not a direct child node of %s" % ( 46 | # old_child, parent_node)) 47 | return new_child 48 | 49 | cls.replace_child_with = _replace_child_with 50 | 51 | 52 | def _generate_adapt_after_inlining(cls): 53 | child_fields = unrolling_iterable(_get_all_child_fields(cls)) 54 | 55 | def _adapt_after_inlining(node, mgenc): 56 | for child_slot in child_fields: 57 | if child_slot.endswith("[*]"): 58 | slot_name = child_slot[:-3] 59 | nodes = getattr(node, slot_name) 60 | if nodes: 61 | for n in nodes: 62 | n.adapt_after_inlining(mgenc) 63 | else: 64 | current = getattr(node, child_slot) 65 | current.adapt_after_inlining(mgenc) 66 | node.handle_inlining(mgenc) 67 | 68 | cls.adapt_after_inlining = _adapt_after_inlining 69 | 70 | 71 | def _generate_adapt_after_outer_inlined(cls): 72 | child_fields = unrolling_iterable(_get_all_child_fields(cls)) 73 | 74 | def _adapt_after_outer_inlined(node, removed_ctx_level, mgenc_with_inlined): 75 | for child_slot in child_fields: 76 | if child_slot.endswith("[*]"): 77 | slot_name = child_slot[:-3] 78 | nodes = getattr(node, slot_name) 79 | if nodes: 80 | for n in nodes: 81 | n.adapt_after_outer_inlined( 82 | removed_ctx_level, mgenc_with_inlined 83 | ) 84 | else: 85 | current = getattr(node, child_slot) 86 | current.adapt_after_outer_inlined(removed_ctx_level, mgenc_with_inlined) 87 | node.handle_outer_inlined(removed_ctx_level, mgenc_with_inlined) 88 | 89 | cls.adapt_after_outer_inlined = _adapt_after_outer_inlined 90 | 91 | 92 | class NodeInitializeMetaClass(type): 93 | def __init__(cls, name, bases, dic): 94 | type.__init__(cls, name, bases, dic) 95 | cls._initialize_node_class() # pylint: disable=no-value-for-parameter 96 | 97 | def _initialize_node_class(cls): 98 | _generate_replace_method(cls) 99 | _generate_adapt_after_inlining(cls) 100 | _generate_adapt_after_outer_inlined(cls) 101 | -------------------------------------------------------------------------------- /src/rtruffle/base_node_2.py: -------------------------------------------------------------------------------- 1 | from rtruffle.abstract_node import AbstractNode, NodeInitializeMetaClass 2 | 3 | 4 | class BaseNode(AbstractNode): 5 | __metaclass__ = NodeInitializeMetaClass 6 | 7 | _immutable_fields_ = ["source_section", "parent"] 8 | _child_nodes_ = [] 9 | -------------------------------------------------------------------------------- /src/rtruffle/base_node_3.py: -------------------------------------------------------------------------------- 1 | from rtruffle.abstract_node import AbstractNode, NodeInitializeMetaClass 2 | 3 | 4 | class BaseNode(AbstractNode, metaclass=NodeInitializeMetaClass): 5 | _immutable_fields_ = ["source_section", "parent"] 6 | _child_nodes_ = [] 7 | -------------------------------------------------------------------------------- /src/rtruffle/node.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from rtruffle.source_section import SourceSection 4 | 5 | if sys.version_info.major > 2: 6 | from rtruffle.base_node_3 import BaseNode 7 | else: 8 | from rtruffle.base_node_2 import BaseNode 9 | 10 | 11 | class Node(BaseNode): 12 | def __init__(self, source_section=None): 13 | assert source_section is None or isinstance(source_section, SourceSection) 14 | self.source_section = source_section 15 | self.parent = None 16 | 17 | def assign_source_section(self, source_section): 18 | assert isinstance(source_section, SourceSection) 19 | if self.source_section: 20 | raise ValueError("Source section already set.") 21 | self.source_section = source_section 22 | 23 | def adopt_child(self, node): 24 | if node: 25 | assert isinstance(node, Node) 26 | node.parent = self 27 | return node 28 | 29 | def adopt_children(self, nodes): 30 | if nodes is None: 31 | return None 32 | 33 | children = nodes[:] # we return here a copy to make it clear to RPython 34 | # that the list is not resized, and, 35 | # the quasi-immutable support does not work on 36 | # element-level, so, we will need to copy the lists 37 | # when replacing child nodes 38 | 39 | for child in children: 40 | child.parent = self 41 | return children 42 | 43 | def replace(self, node): 44 | if node: 45 | self.parent.replace_child_with(self, node) 46 | node.parent = self.parent 47 | return node 48 | return None 49 | 50 | def __str__(self): 51 | return "%s(%s)" % (self.__class__.__name__, self.source_section) 52 | -------------------------------------------------------------------------------- /src/rtruffle/source_section.py: -------------------------------------------------------------------------------- 1 | class SourceCoordinate(object): 2 | _immutable_fields_ = ["start_line", "start_column", "char_idx"] 3 | 4 | def __init__(self, start_line, start_column, char_idx): 5 | self.start_line = start_line 6 | self.start_column = start_column 7 | self.char_idx = char_idx 8 | 9 | 10 | class SourceSection(object): 11 | _immutable_fields_ = ["source", "identifier", "coord", "char_length"] 12 | 13 | def __init__( 14 | self, 15 | source=None, 16 | identifier=None, 17 | coord=None, 18 | char_length=0, 19 | file_name=None, 20 | source_section=None, 21 | ): 22 | if source_section: 23 | self.source = source_section.source 24 | self.coord = source_section.coord 25 | self.char_length = source_section.char_length 26 | self.file = source_section.file 27 | else: 28 | self.source = source 29 | self.coord = coord 30 | self.char_length = char_length 31 | self.file = file_name 32 | self.identifier = identifier 33 | 34 | def __str__(self): 35 | return "%s:%d:%d" % (self.file, self.coord.start_line, self.coord.start_column) 36 | -------------------------------------------------------------------------------- /src/som/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOM-st/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/__init__.py -------------------------------------------------------------------------------- /src/som/compiler/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOM-st/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/compiler/__init__.py -------------------------------------------------------------------------------- /src/som/compiler/ast/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOM-st/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/compiler/ast/__init__.py -------------------------------------------------------------------------------- /src/som/compiler/ast/disassembler.py: -------------------------------------------------------------------------------- 1 | from som.vm.universe import error_print, error_println 2 | 3 | 4 | def dump(clazz): 5 | for inv in clazz.get_instance_invokables_for_disassembler(): 6 | # output header and skip if the Invokable is a Primitive 7 | error_print(str(clazz.get_name()) + ">>" + str(inv.get_signature()) + " = ") 8 | 9 | if inv.is_primitive(): 10 | error_println("") 11 | continue 12 | 13 | # output actual method 14 | dump_method(inv, "\t") 15 | 16 | 17 | def dump_method(_, indent): 18 | error_println("(") 19 | error_println(indent + indent + "TODO") 20 | error_println(indent + ")") 21 | -------------------------------------------------------------------------------- /src/som/compiler/ast/method_generation_context.py: -------------------------------------------------------------------------------- 1 | from rtruffle.source_section import SourceSection 2 | 3 | from som.compiler.method_generation_context import MethodGenerationContextBase 4 | 5 | from som.interpreter.ast.nodes.field_node import create_write_node, create_read_node 6 | from som.interpreter.ast.nodes.global_read_node import create_global_node 7 | from som.interpreter.ast.nodes.return_non_local_node import CatchNonLocalReturnNode 8 | 9 | from som.vmobjects.primitive import empty_primitive 10 | from som.vmobjects.method_ast import AstMethod 11 | 12 | 13 | class MethodGenerationContext(MethodGenerationContextBase): 14 | def __init__(self, universe, holder, outer): 15 | MethodGenerationContextBase.__init__(self, universe, holder, outer) 16 | 17 | self._embedded_block_methods = [] 18 | 19 | def add_embedded_block_method(self, block_method): 20 | self._embedded_block_methods.append(block_method) 21 | 22 | def assemble(self, method_body): 23 | if self._primitive: 24 | return empty_primitive(self.signature.get_embedded_string()) 25 | 26 | if self.needs_to_catch_non_local_returns: 27 | method_body = CatchNonLocalReturnNode( 28 | method_body, method_body.source_section 29 | ) 30 | 31 | trivial_method = method_body.create_trivial_method(self.signature) 32 | if trivial_method is not None: 33 | return trivial_method 34 | 35 | arg_inner_access, size_frame, size_inner = self.prepare_frame() 36 | 37 | return AstMethod( 38 | self.signature, 39 | method_body, 40 | arg_inner_access, 41 | size_frame, 42 | size_inner, 43 | # copy list to make it immutable for RPython 44 | self._embedded_block_methods[:], 45 | self._get_source_section_for_method(method_body), 46 | self.lexical_scope, 47 | ) 48 | 49 | def _get_source_section_for_method(self, expr): 50 | src_body = expr.source_section 51 | assert isinstance(src_body, SourceSection) 52 | src_method = SourceSection( 53 | identifier="%s>>#%s" 54 | % ( 55 | self.holder.name.get_embedded_string(), 56 | self.signature.get_embedded_string(), 57 | ), 58 | source_section=src_body, 59 | ) 60 | return src_method 61 | 62 | def get_outer_self_context_level(self): 63 | level = 0 64 | ctx = self.outer_genc 65 | while ctx is not None: 66 | ctx = ctx.outer_genc 67 | level += 1 68 | return level 69 | 70 | def get_context_level(self, var_name): 71 | if var_name in self._locals or var_name in self._arguments: 72 | return 0 73 | assert self.outer_genc is not None 74 | return 1 + self.outer_genc.get_context_level(var_name) 75 | 76 | def get_variable(self, var_name): 77 | if var_name in self._locals: 78 | return self._locals[var_name] 79 | 80 | if var_name in self._arguments: 81 | return self._arguments[var_name] 82 | 83 | if self.outer_genc: 84 | outer_var = self.outer_genc.get_variable(var_name) 85 | if outer_var: 86 | self._accesses_variables_of_outer_context = True 87 | return outer_var 88 | return None 89 | 90 | def get_local(self, var_name): 91 | if var_name in self._locals: 92 | return self._locals[var_name] 93 | 94 | if self.outer_genc: 95 | outer_local = self.outer_genc.get_local(var_name) 96 | if outer_local: 97 | self._accesses_variables_of_outer_context = True 98 | return outer_local 99 | return None 100 | 101 | def get_self_read(self): 102 | return self.get_variable("self").get_read_node(self.get_context_level("self")) 103 | 104 | def get_object_field_read(self, field_name): 105 | if not self.has_field(field_name): 106 | return None 107 | return create_read_node(self.get_self_read(), self.get_field_index(field_name)) 108 | 109 | def get_global_read(self, var_name): 110 | return create_global_node(var_name, self.universe, self, None) 111 | 112 | def get_object_field_write(self, field_name, exp): 113 | if not self.has_field(field_name): 114 | return None 115 | return create_write_node( 116 | self.get_self_read(), exp, self.get_field_index(field_name) 117 | ) 118 | -------------------------------------------------------------------------------- /src/som/compiler/bc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOM-st/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/compiler/bc/__init__.py -------------------------------------------------------------------------------- /src/som/compiler/class_generation_context.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | 3 | from som.vm.symbols import symbol_for 4 | from som.vmobjects.array import Array 5 | from som.vmobjects.clazz import Class 6 | 7 | 8 | class ClassGenerationContext(object): 9 | def __init__(self, universe): 10 | self.universe = universe 11 | 12 | self.name = None 13 | self._super_class = None 14 | self._class_side = False # to be overridden 15 | self._instance_fields = [] 16 | self._class_fields = [] 17 | 18 | self._instance_methods = OrderedDict() 19 | self._class_methods = OrderedDict() 20 | 21 | self._instance_has_primitives = False 22 | self._class_has_primitives = False 23 | 24 | def __str__(self): 25 | result = "CGenc(" 26 | if self.name: 27 | result += self.name.get_embedded_string() 28 | result += ")" 29 | return result 30 | 31 | def get_super_class(self): 32 | if self._class_side: 33 | return self._super_class.get_class(self.universe) 34 | return self._super_class 35 | 36 | def set_super_class(self, super_class): 37 | self._super_class = super_class 38 | self._set_instance_fields_of_super(super_class.get_instance_fields()) 39 | self._set_class_fields_of_super( 40 | super_class.get_class(self.universe).get_instance_fields() 41 | ) 42 | 43 | def _set_instance_fields_of_super(self, field_names): 44 | for i in range(0, field_names.get_number_of_indexable_fields()): 45 | self._instance_fields.append(field_names.get_indexable_field(i)) 46 | 47 | def _set_class_fields_of_super(self, field_names): 48 | for i in range(0, field_names.get_number_of_indexable_fields()): 49 | self._class_fields.append(field_names.get_indexable_field(i)) 50 | 51 | def add_instance_method(self, method): 52 | self._instance_methods[method.get_signature()] = method 53 | if method.is_primitive(): 54 | self._instance_has_primitives = True 55 | 56 | def switch_to_class_side(self): 57 | self._class_side = True 58 | 59 | def add_class_method(self, method): 60 | self._class_methods[method.get_signature()] = method 61 | if method.is_primitive(): 62 | self._class_has_primitives = True 63 | 64 | def add_instance_field(self, field): 65 | self._instance_fields.append(field) 66 | 67 | def get_instance_field_name(self, _idx): 68 | return "not yet implemented" 69 | # TODO: return self._instance_fields[idx] and support of super classes, I think 70 | 71 | def add_class_field(self, field): 72 | self._class_fields.append(field) 73 | 74 | def has_field(self, field): 75 | return field in ( 76 | self._class_fields if self.is_class_side() else self._instance_fields 77 | ) 78 | 79 | def get_field_index(self, field): 80 | if self.is_class_side(): 81 | return self._class_fields.index(field) 82 | return self._instance_fields.index(field) 83 | 84 | def is_class_side(self): 85 | return self._class_side 86 | 87 | def assemble(self): 88 | # build class class name 89 | cc_name = self.name.get_embedded_string() + " class" 90 | 91 | # Allocate the class of the resulting class 92 | result_class = Class( 93 | self.universe.metaclass_class.get_number_of_instance_fields(), 94 | self.universe.metaclass_class, 95 | ) 96 | 97 | # Initialize the class of the resulting class 98 | result_class.set_instance_fields(Array.from_objects(self._class_fields[:])) 99 | result_class.set_instance_invokables( 100 | self._class_methods, self._class_has_primitives 101 | ) 102 | result_class.set_name(symbol_for(cc_name)) 103 | 104 | super_m_class = self._super_class.get_class(self.universe) 105 | result_class.set_super_class(super_m_class) 106 | 107 | # Allocate the resulting class 108 | result = Class(result_class.get_number_of_instance_fields(), result_class) 109 | 110 | # Initialize the resulting class 111 | result.set_name(self.name) 112 | result.set_super_class(self._super_class) 113 | result.set_instance_fields(Array.from_objects(self._instance_fields[:])) 114 | result.set_instance_invokables( 115 | self._instance_methods, self._instance_has_primitives 116 | ) 117 | 118 | return result 119 | 120 | def assemble_system_class(self, system_class): 121 | system_class.set_instance_invokables( 122 | self._instance_methods, self._instance_has_primitives 123 | ) 124 | system_class.set_instance_fields(Array.from_objects(self._instance_fields[:])) 125 | 126 | # class-bound == class-instance-bound 127 | super_m_class = system_class.get_class(self.universe) 128 | super_m_class.set_instance_invokables( 129 | self._class_methods, self._class_has_primitives 130 | ) 131 | super_m_class.set_instance_fields(Array.from_objects(self._class_fields[:])) 132 | -------------------------------------------------------------------------------- /src/som/compiler/disassembler.py: -------------------------------------------------------------------------------- 1 | from som.interp_type import is_ast_interpreter 2 | 3 | if is_ast_interpreter(): 4 | from som.compiler.ast.disassembler import dump as d_fn 5 | else: 6 | from som.compiler.bc.disassembler import dump as d_fn 7 | 8 | dump = d_fn 9 | -------------------------------------------------------------------------------- /src/som/compiler/lexical_scope.py: -------------------------------------------------------------------------------- 1 | from rlib.debug import make_sure_not_resized 2 | 3 | 4 | class LexicalScope(object): 5 | _immutable_fields_ = ["outer", "arguments[*]", "locals[*]"] 6 | 7 | def __init__(self, outer, arguments, local_vars): 8 | self.outer = outer 9 | self.arguments = arguments 10 | self.locals = local_vars 11 | assert self.locals is not None 12 | assert self.arguments is not None 13 | 14 | def get_argument(self, idx, ctx_level): 15 | if ctx_level > 0: 16 | return self.outer.get_argument(idx, ctx_level - 1) 17 | return self.arguments[idx] 18 | 19 | def get_local(self, idx, ctx_level): 20 | assert self.locals is not None 21 | if ctx_level > 0: 22 | return self.outer.get_local(idx, ctx_level - 1) 23 | return self.locals[idx] 24 | 25 | def add_inlined_locals(self, local_vars): 26 | if not local_vars: 27 | return 28 | 29 | combined = [None] * (len(self.locals) + len(local_vars)) 30 | 31 | i = 0 32 | for local in self.locals: 33 | combined[i] = local 34 | i += 1 35 | 36 | for local in local_vars: 37 | combined[i] = local 38 | i += 1 39 | 40 | self.locals = combined 41 | make_sure_not_resized(self.locals) 42 | 43 | def drop_inlined_scope(self): 44 | """ 45 | This removes the inlined scope from the chain. 46 | Removal is done exactly once, after all embedded blocks 47 | were adapted. 48 | """ 49 | assert self.outer.outer 50 | self.outer = self.outer.outer 51 | -------------------------------------------------------------------------------- /src/som/compiler/parse_error.py: -------------------------------------------------------------------------------- 1 | from som.compiler.symbol import Symbol, symbol_as_str 2 | 3 | 4 | class ParseError(Exception): 5 | def __init__(self, message, expected_sym, parser): # pylint: disable=W 6 | self._message = message 7 | self._source_coordinate = parser._lexer.get_source_coordinate() 8 | self._text = parser._text 9 | self._raw_buffer = parser._lexer.get_raw_buffer() 10 | self._file_name = parser._file_name 11 | self._expected_sym = expected_sym 12 | self._found_sym = parser._sym 13 | 14 | def _is_printable_symbol(self): 15 | return ( 16 | self._found_sym == Symbol.Integer 17 | or self._found_sym == Symbol.Double 18 | or self._found_sym >= Symbol.STString 19 | ) 20 | 21 | def _expected_sym_str(self): 22 | return symbol_as_str(self._expected_sym) 23 | 24 | def __str__(self): 25 | msg = "%(file)s:%(line)d:%(column)d: error: " + self._message 26 | if self._is_printable_symbol(): 27 | found = "%s (%s)" % (symbol_as_str(self._found_sym), self._text) 28 | else: 29 | found = symbol_as_str(self._found_sym) 30 | msg += ": %s" % self._raw_buffer 31 | 32 | expected = self._expected_sym_str() 33 | 34 | return msg % { 35 | "file": self._file_name, 36 | "line": self._source_coordinate.start_line, 37 | "column": self._source_coordinate.start_column, 38 | "expected": expected, 39 | "found": found, 40 | } 41 | 42 | 43 | class ParseErrorSymList(ParseError): 44 | def __init__(self, message, expected_syms, parser): 45 | ParseError.__init__(self, message, 0, parser) 46 | self._expected_syms = expected_syms 47 | 48 | def _expected_sym_str(self): 49 | return ", ".join([symbol_as_str(x) for x in self._expected_syms]) 50 | -------------------------------------------------------------------------------- /src/som/compiler/sourcecode_compiler.py: -------------------------------------------------------------------------------- 1 | import os 2 | from rlib.streamio import open_file_as_stream 3 | from rlib.string_stream import StringStream 4 | 5 | from som.compiler.class_generation_context import ClassGenerationContext 6 | from som.interp_type import is_ast_interpreter 7 | 8 | if is_ast_interpreter(): 9 | from som.compiler.ast.parser import Parser 10 | else: 11 | from som.compiler.bc.parser import Parser 12 | 13 | 14 | def compile_class_from_file(path, filename, system_class, universe): 15 | fname = path + os.sep + filename + ".som" 16 | 17 | try: 18 | input_file = open_file_as_stream(fname, "r") 19 | try: 20 | parser = Parser(input_file, fname, universe) 21 | result = _compile(parser, system_class, universe) 22 | finally: 23 | input_file.close() 24 | except OSError: 25 | raise IOError() 26 | 27 | cname = result.get_name() 28 | cname_str = cname.get_embedded_string() 29 | 30 | if filename != cname_str: 31 | from som.vm.universe import error_println 32 | 33 | error_println( 34 | "File name %s does not match class name %s." % (filename, cname_str) 35 | ) 36 | universe.exit(1) 37 | 38 | return result 39 | 40 | 41 | def compile_class_from_string(stream, system_class, universe): 42 | parser = Parser(StringStream(stream), "$str", universe) 43 | result = _compile(parser, system_class, universe) 44 | return result 45 | 46 | 47 | def _compile(parser, system_class, universe): 48 | cgc = ClassGenerationContext(universe) 49 | 50 | result = system_class 51 | parser.classdef(cgc) 52 | 53 | if not system_class: 54 | result = cgc.assemble() 55 | else: 56 | cgc.assemble_system_class(result) 57 | 58 | return result 59 | -------------------------------------------------------------------------------- /src/som/compiler/symbol.py: -------------------------------------------------------------------------------- 1 | # Symbol is a 'lightweight' enum, in Python 3.4, we could use Enum as superclass 2 | class Symbol(object): 3 | NONE = -1 4 | Integer = 0 5 | Double = 1 6 | Not = 2 7 | And = 3 8 | Or = 4 9 | Star = 5 10 | Div = 6 11 | Mod = 7 12 | Plus = 8 13 | Minus = 9 14 | Equal = 10 15 | More = 11 16 | Less = 12 17 | Comma = 13 18 | At = 14 19 | Per = 15 20 | NewBlock = 16 21 | EndBlock = 17 22 | Colon = 18 23 | Period = 19 24 | Exit = 20 25 | Assign = 21 26 | NewTerm = 22 27 | EndTerm = 23 28 | Pound = 24 29 | Primitive = 25 30 | Separator = 26 31 | STString = 27 32 | Identifier = 28 33 | Keyword = 29 34 | KeywordSequence = 30 35 | OperatorSequence = 31 36 | 37 | 38 | def _sorted_symbols(cls): 39 | "NOT_RPYTHON" 40 | """This function is only called a single time, at load time of this module. 41 | For RPython, this means, during translation of the module. 42 | """ 43 | return [ 44 | key 45 | for value, key in sorted( 46 | [ 47 | (value, key) 48 | for key, value in cls.__dict__.items() 49 | if isinstance(value, int) 50 | ] 51 | ) 52 | ] 53 | 54 | 55 | _symbols = _sorted_symbols(Symbol) 56 | 57 | 58 | def symbol_as_str(symbol): 59 | index = symbol + 1 60 | if index > len(_symbols): 61 | raise ValueError("No Symbol defined for the value %d." % symbol) 62 | return _symbols[index] 63 | -------------------------------------------------------------------------------- /src/som/interp_type.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | from rlib import jit 5 | 6 | _interp_type_str = os.getenv("SOM_INTERP", None) 7 | 8 | _AST = 1 9 | _BC = 2 10 | _UNKNOWN = 3 11 | 12 | 13 | def _get_interp_type(): 14 | if _interp_type_str is None or not (_interp_type_str in ["AST", "BC"]): 15 | print( 16 | "Type of interpreter not set. Please set the SOM_INTERP environment variable" 17 | ) 18 | print("\tSOM_INTERP=AST Use an Abstract Syntax Tree interpreter") 19 | print("\tSOM_INTERP=BC Use a Bytecode interpreter") 20 | sys.exit(1) 21 | if _interp_type_str == "AST": 22 | return _AST 23 | if _interp_type_str == "BC": 24 | return _BC 25 | return _UNKNOWN 26 | 27 | 28 | _INTERP_TYPE = _get_interp_type() 29 | 30 | 31 | @jit.elidable 32 | def is_bytecode_interpreter(): 33 | return _INTERP_TYPE == _BC 34 | 35 | 36 | @jit.elidable 37 | def is_ast_interpreter(): 38 | return _INTERP_TYPE == _AST 39 | -------------------------------------------------------------------------------- /src/som/interpreter/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOM-st/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/interpreter/__init__.py -------------------------------------------------------------------------------- /src/som/interpreter/ast/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOM-st/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/interpreter/ast/__init__.py -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOM-st/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/interpreter/ast/nodes/__init__.py -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/block_node.py: -------------------------------------------------------------------------------- 1 | from som.interpreter.ast.frame import get_inner_as_context 2 | from som.vmobjects.block_ast import AstBlock 3 | from som.interpreter.ast.nodes.literal_node import LiteralNode 4 | 5 | 6 | class BlockNode(LiteralNode): 7 | _immutable_fields_ = ["universe"] 8 | 9 | def __init__(self, value, universe, source_section=None): 10 | LiteralNode.__init__(self, value, source_section) 11 | self.universe = universe 12 | 13 | def execute(self, _frame): 14 | return AstBlock(self._value, None) 15 | 16 | def create_trivial_method(self, signature): 17 | return None 18 | 19 | def get_method(self): 20 | return self._value 21 | 22 | 23 | class BlockNodeWithContext(BlockNode): 24 | def __init__(self, value, universe, source_section=None): 25 | BlockNode.__init__(self, value, universe, source_section) 26 | 27 | def execute(self, frame): 28 | return AstBlock(self._value, get_inner_as_context(frame)) 29 | 30 | def handle_inlining(self, mgenc): 31 | self._value.adapt_after_outer_inlined(1, mgenc) 32 | 33 | def handle_outer_inlined(self, removed_ctx_level, mgenc_with_inlined): 34 | self._value.adapt_after_outer_inlined(removed_ctx_level + 1, mgenc_with_inlined) 35 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/contextual_node.py: -------------------------------------------------------------------------------- 1 | from rlib.jit import unroll_safe 2 | from som.interpreter.ast.frame import FRAME_AND_INNER_RCVR_IDX, read_frame 3 | 4 | from som.interpreter.ast.nodes.expression_node import ExpressionNode 5 | 6 | 7 | class ContextualNode(ExpressionNode): 8 | _immutable_fields_ = ["_context_level"] 9 | 10 | def __init__(self, context_level, source_section=None): 11 | ExpressionNode.__init__(self, source_section) 12 | assert context_level >= 0 13 | self._context_level = context_level 14 | 15 | def get_context_level(self): 16 | return self._context_level 17 | 18 | def accesses_outer_context(self): 19 | return self._context_level > 0 20 | 21 | @unroll_safe 22 | def determine_block(self, frame): 23 | assert self._context_level > 0 24 | 25 | block = read_frame(frame, FRAME_AND_INNER_RCVR_IDX) 26 | for _ in range(0, self._context_level - 1): 27 | block = block.get_from_outer(FRAME_AND_INNER_RCVR_IDX) 28 | return block 29 | 30 | @unroll_safe 31 | def determine_outer_self(self, frame): 32 | outer_self = read_frame(frame, FRAME_AND_INNER_RCVR_IDX) 33 | for _ in range(0, self._context_level): 34 | outer_self = outer_self.get_from_outer(FRAME_AND_INNER_RCVR_IDX) 35 | return outer_self 36 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/dispatch.py: -------------------------------------------------------------------------------- 1 | from som.interpreter.send import lookup_and_send_3 2 | from som.vm.symbols import symbol_for 3 | 4 | from som.vmobjects.array import Array 5 | 6 | 7 | INLINE_CACHE_SIZE = 6 8 | 9 | 10 | class _AbstractDispatchNode(object): 11 | _immutable_fields_ = ["expected_layout", "next_entry?"] 12 | _child_nodes_ = ["next_entry"] 13 | 14 | def __init__(self, expected_layout, next_entry): 15 | self.expected_layout = expected_layout 16 | self.next_entry = next_entry 17 | 18 | 19 | class GenericDispatchNode(_AbstractDispatchNode): 20 | _immutable_fields_ = ["universe", "_selector"] 21 | 22 | def __init__(self, selector, universe): 23 | """ 24 | The Generic Dispatch Node sets expected_layout to None. 25 | This is used as a check in the dispatch, to recognize the generic case. 26 | """ 27 | _AbstractDispatchNode.__init__(self, None, None) 28 | self.universe = universe 29 | self._selector = selector 30 | 31 | def execute_dispatch(self, rcvr, args): 32 | method = rcvr.get_object_layout(self.universe).lookup_invokable(self._selector) 33 | if method is not None: 34 | return method.invoke(rcvr, args) 35 | return self._send_dnu(rcvr, args) 36 | 37 | def _send_dnu(self, rcvr, args): 38 | # Won't use DNU caching here, because it's a megamorphic node 39 | return lookup_and_send_3( 40 | rcvr, 41 | self._selector, 42 | Array.from_values(args), 43 | "doesNotUnderstand:arguments:", 44 | ) 45 | 46 | def dispatch_1(self, rcvr): 47 | method = rcvr.get_object_layout(self.universe).lookup_invokable(self._selector) 48 | if method is not None: 49 | return method.invoke_1(rcvr) 50 | return self._send_dnu(rcvr, []) 51 | 52 | def dispatch_2(self, rcvr, arg): 53 | method = rcvr.get_object_layout(self.universe).lookup_invokable(self._selector) 54 | if method is not None: 55 | return method.invoke_2(rcvr, arg) 56 | return self._send_dnu(rcvr, [arg]) 57 | 58 | def dispatch_3(self, rcvr, arg1, arg2): 59 | method = rcvr.get_object_layout(self.universe).lookup_invokable(self._selector) 60 | if method is not None: 61 | return method.invoke_3(rcvr, arg1, arg2) 62 | return self._send_dnu(rcvr, [arg1, arg2]) 63 | 64 | def dispatch_args(self, rcvr, args): 65 | method = rcvr.get_object_layout(self.universe).lookup_invokable(self._selector) 66 | if method is not None: 67 | return method.invoke_args(rcvr, args) 68 | return self._send_dnu(rcvr, args) 69 | 70 | def dispatch_n_bc(self, stack, stack_ptr, rcvr): 71 | method = rcvr.get_object_layout(self.universe).lookup_invokable(self._selector) 72 | if method is not None: 73 | return method.invoke_n(stack, stack_ptr) 74 | from som.interpreter.bc.interpreter import send_does_not_understand 75 | 76 | return send_does_not_understand(rcvr, self._selector, stack, stack_ptr) 77 | 78 | 79 | class CachedDispatchNode(_AbstractDispatchNode): 80 | _immutable_fields_ = ["_cached_method"] 81 | 82 | def __init__(self, rcvr_class, method, next_entry): 83 | _AbstractDispatchNode.__init__(self, rcvr_class, next_entry) 84 | self._cached_method = method 85 | 86 | def dispatch_1(self, rcvr): 87 | return self._cached_method.invoke_1(rcvr) 88 | 89 | def dispatch_2(self, rcvr, arg): 90 | return self._cached_method.invoke_2(rcvr, arg) 91 | 92 | def dispatch_3(self, rcvr, arg1, arg2): 93 | return self._cached_method.invoke_3(rcvr, arg1, arg2) 94 | 95 | def dispatch_args(self, rcvr, args): 96 | return self._cached_method.invoke_args(rcvr, args) 97 | 98 | def dispatch_n_bc(self, stack, stack_ptr, _rcvr): 99 | return self._cached_method.invoke_n(stack, stack_ptr) 100 | 101 | 102 | class CachedDnuNode(_AbstractDispatchNode): 103 | _immutable_fields_ = ["_selector", "_cached_method"] 104 | 105 | def __init__(self, selector, layout, next_entry): 106 | _AbstractDispatchNode.__init__( 107 | self, 108 | layout, 109 | next_entry, 110 | ) 111 | self._selector = selector 112 | self._cached_method = layout.lookup_invokable( 113 | symbol_for("doesNotUnderstand:arguments:") 114 | ) 115 | 116 | def dispatch_1(self, rcvr): 117 | return self._cached_method.invoke_3(rcvr, self._selector, Array.from_size(0)) 118 | 119 | def dispatch_2(self, rcvr, arg): 120 | return self._cached_method.invoke_3( 121 | rcvr, self._selector, Array.from_values([arg]) 122 | ) 123 | 124 | def dispatch_3(self, rcvr, arg1, arg2): 125 | return self._cached_method.invoke_3( 126 | rcvr, self._selector, Array.from_values([arg1, arg2]) 127 | ) 128 | 129 | def dispatch_args(self, rcvr, args): 130 | return self._cached_method.invoke_3( 131 | rcvr, self._selector, Array.from_values(args) 132 | ) 133 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/expression_node.py: -------------------------------------------------------------------------------- 1 | from rtruffle.node import Node 2 | 3 | 4 | class ExpressionNode(Node): 5 | def __init__(self, source_section): 6 | Node.__init__(self, source_section) 7 | 8 | def create_trivial_method(self, _signature): 9 | return None 10 | 11 | def is_trivial_in_sequence(self): 12 | return False 13 | 14 | def handle_inlining(self, mgenc): # pylint: disable=W 15 | pass 16 | 17 | def handle_outer_inlined( 18 | self, removed_ctx_level, mgenc_with_inlined 19 | ): # pylint: disable=W 20 | pass 21 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/field_node.py: -------------------------------------------------------------------------------- 1 | from som.compiler.ast.variable import Argument 2 | from som.interpreter.ast.nodes.contextual_node import ContextualNode 3 | from som.interpreter.ast.nodes.expression_node import ExpressionNode 4 | from som.interpreter.ast.nodes.specialized.int_inc_node import IntIncrementNode 5 | from som.interpreter.ast.nodes.variable_node import UninitializedReadNode 6 | from som.vmobjects.method_trivial import FieldRead, FieldWrite 7 | 8 | MAX_FIELD_ACCESS_CHAIN_LENGTH = 6 9 | 10 | 11 | class _AbstractFieldNode(ExpressionNode): 12 | _immutable_fields_ = ["_self_exp?", "field_idx"] 13 | _child_nodes_ = ["_self_exp"] 14 | 15 | def __init__(self, self_exp, field_idx, source_section): 16 | ExpressionNode.__init__(self, source_section) 17 | self._self_exp = self.adopt_child(self_exp) 18 | self.field_idx = field_idx 19 | 20 | 21 | class FieldReadNode(_AbstractFieldNode): 22 | def execute(self, frame): 23 | self_obj = self._self_exp.execute(frame) 24 | return self_obj.get_field(self.field_idx) 25 | 26 | def create_trivial_method(self, signature): 27 | if isinstance(self._self_exp, ContextualNode): 28 | ctx_level = self._self_exp.get_context_level() 29 | else: 30 | ctx_level = 0 31 | return FieldRead(signature, self.field_idx, ctx_level) 32 | 33 | 34 | class FieldWriteNode(_AbstractFieldNode): 35 | _immutable_fields_ = ["_value_exp?"] 36 | _child_nodes_ = ["_value_exp"] 37 | 38 | def __init__(self, self_exp, value_exp, field_idx, source_section): 39 | _AbstractFieldNode.__init__(self, self_exp, field_idx, source_section) 40 | self._value_exp = self.adopt_child(value_exp) 41 | 42 | def execute(self, frame): 43 | self_obj = self._self_exp.execute(frame) 44 | value = self._value_exp.execute(frame) 45 | self_obj.set_field(self.field_idx, value) 46 | return value 47 | 48 | def create_trivial_method(self, signature): 49 | if isinstance(self._self_exp, ContextualNode): 50 | # block methods not currently supported 51 | return None 52 | 53 | val_exp = self._value_exp 54 | if not isinstance(val_exp, UninitializedReadNode): 55 | return None 56 | 57 | var = val_exp.var 58 | if isinstance(var, Argument): 59 | arg_idx = var.idx 60 | return FieldWrite(signature, self.field_idx, arg_idx) 61 | return None 62 | 63 | def is_trivial_in_sequence(self): 64 | return True 65 | 66 | 67 | class FieldIncrementNode(_AbstractFieldNode): 68 | def execute(self, frame): 69 | self_obj = self._self_exp.execute(frame) 70 | return self_obj.inc_field(self.field_idx) 71 | 72 | 73 | def create_read_node(self_exp, index, source_section=None): 74 | return FieldReadNode(self_exp, index, source_section) 75 | 76 | 77 | def create_write_node(self_exp, value_exp, index, source_section=None): 78 | if isinstance(value_exp, IntIncrementNode) and value_exp.does_access_field(index): 79 | return FieldIncrementNode(self_exp, index, source_section) 80 | return FieldWriteNode(self_exp, value_exp, index, source_section) 81 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/global_read_node.py: -------------------------------------------------------------------------------- 1 | from som.interpreter.ast.nodes.contextual_node import ContextualNode 2 | from som.interpreter.ast.nodes.literal_node import LiteralNode 3 | from som.interpreter.send import lookup_and_send_2 4 | from som.vm.globals import nilObject, trueObject, falseObject 5 | 6 | from som.interpreter.ast.nodes.expression_node import ExpressionNode 7 | from som.vm.symbols import sym_false, sym_true, sym_nil 8 | from som.vmobjects.method_trivial import GlobalRead 9 | 10 | 11 | def create_global_node(global_name, universe, mgenc, source_section): 12 | if global_name is sym_true: 13 | return LiteralNode(trueObject, source_section) 14 | if global_name is sym_false: 15 | return LiteralNode(falseObject, source_section) 16 | if global_name is sym_nil: 17 | return LiteralNode(nilObject, source_section) 18 | 19 | assoc = universe.get_globals_association_or_none(global_name) 20 | if assoc is not None: 21 | return _CachedGlobalReadNode(assoc, source_section) 22 | 23 | context_level = mgenc.get_context_level("self") 24 | mgenc.mark_self_as_accessed_from_outer_context() 25 | return _UninitializedGlobalReadNode( 26 | global_name, universe, context_level, source_section 27 | ) 28 | 29 | 30 | class _UninitializedGlobalReadNode(ContextualNode): 31 | _immutable_fields_ = ["_global_name", "universe"] 32 | 33 | def __init__(self, global_name, universe, context_level, source_section=None): 34 | ContextualNode.__init__(self, context_level, source_section) 35 | self._global_name = global_name 36 | self.universe = universe 37 | 38 | def execute(self, frame): 39 | if self.universe.has_global(self._global_name): 40 | return self._specialize().execute(frame) 41 | return lookup_and_send_2( 42 | self.determine_outer_self(frame), 43 | self._global_name, 44 | "unknownGlobal:", 45 | ) 46 | 47 | def _specialize(self): 48 | assoc = self.universe.get_globals_association(self._global_name) 49 | cached = _CachedGlobalReadNode(assoc, self.source_section) 50 | return self.replace(cached) 51 | 52 | def create_trivial_method(self, signature): 53 | return GlobalRead( 54 | signature, self._global_name, self._context_level, self.universe 55 | ) 56 | 57 | def handle_inlining(self, mgenc): 58 | self._context_level -= 1 59 | 60 | 61 | class _CachedGlobalReadNode(ExpressionNode): 62 | _immutable_fields_ = ["_assoc"] 63 | 64 | def __init__(self, assoc, source_section): 65 | ExpressionNode.__init__(self, source_section) 66 | self._assoc = assoc 67 | 68 | def execute(self, _frame): 69 | return self._assoc.value 70 | 71 | def create_trivial_method(self, signature): 72 | return GlobalRead(signature, None, 0, None, self._assoc) 73 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/literal_node.py: -------------------------------------------------------------------------------- 1 | from som.interpreter.ast.nodes.expression_node import ExpressionNode 2 | 3 | 4 | class LiteralNode(ExpressionNode): 5 | _immutable_fields_ = ["_value"] 6 | 7 | def __init__(self, value, source_section=None): 8 | ExpressionNode.__init__(self, source_section) 9 | self._value = value 10 | 11 | def execute(self, _frame): 12 | return self._value 13 | 14 | def create_trivial_method(self, signature): 15 | from som.vmobjects.method_trivial import LiteralReturn 16 | 17 | return LiteralReturn(signature, self._value) 18 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/message/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOM-st/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/interpreter/ast/nodes/message/__init__.py -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/message/abstract_node.py: -------------------------------------------------------------------------------- 1 | from rlib.debug import make_sure_not_resized 2 | from rlib.jit import unroll_safe 3 | 4 | from som.interpreter.ast.nodes.expression_node import ExpressionNode 5 | 6 | from som.vmobjects.abstract_object import AbstractObject 7 | 8 | 9 | class AbstractMessageNode(ExpressionNode): 10 | _immutable_fields_ = ["_selector", "universe", "_rcvr_expr?", "_arg_exprs?[*]"] 11 | _child_nodes_ = ["_rcvr_expr", "_arg_exprs[*]"] 12 | 13 | def __init__(self, selector, universe, rcvr_expr, arg_exprs, source_section=None): 14 | ExpressionNode.__init__(self, source_section) 15 | self._selector = selector 16 | self.universe = universe 17 | assert arg_exprs is not None 18 | make_sure_not_resized(arg_exprs) 19 | self._rcvr_expr = self.adopt_child(rcvr_expr) 20 | self._arg_exprs = self.adopt_children(arg_exprs) 21 | 22 | @unroll_safe 23 | def _evaluate_rcvr_and_args(self, frame): 24 | rcvr = self._rcvr_expr.execute(frame) 25 | assert isinstance(rcvr, AbstractObject) 26 | if len(self._arg_exprs) == 0: 27 | args = [] 28 | else: 29 | args = [arg_exp.execute(frame) for arg_exp in self._arg_exprs] 30 | return rcvr, args 31 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/message/super_node.py: -------------------------------------------------------------------------------- 1 | from som.interpreter.ast.nodes.expression_node import ExpressionNode 2 | 3 | 4 | class AbstractSuperMessageNode(ExpressionNode): 5 | _immutable_fields_ = ["_method?", "_super_class", "_selector", "_rcvr_expr?"] 6 | _child_nodes_ = ["_rcvr_expr"] 7 | 8 | def __init__(self, selector, rcvr_expr, super_class, source_section=None): 9 | ExpressionNode.__init__(self, source_section) 10 | self._selector = selector 11 | 12 | self._method = None 13 | self._super_class = super_class 14 | self._selector = selector 15 | self._rcvr_expr = self.adopt_child(rcvr_expr) 16 | 17 | 18 | class UnarySuper(AbstractSuperMessageNode): 19 | def execute(self, frame): 20 | if self._method is None: 21 | self._method = self._super_class.lookup_invokable(self._selector) 22 | if not self._method: 23 | raise Exception("Not yet implemented") 24 | 25 | rcvr = self._rcvr_expr.execute(frame) 26 | return self._method.invoke_1(rcvr) 27 | 28 | 29 | class BinarySuper(AbstractSuperMessageNode): 30 | _immutable_fields_ = ["_arg_expr?"] 31 | _child_nodes_ = ["_arg_expr"] 32 | 33 | def __init__(self, selector, rcvr_expr, arg_expr, super_class, source_section=None): 34 | AbstractSuperMessageNode.__init__( 35 | self, selector, rcvr_expr, super_class, source_section 36 | ) 37 | self._arg_expr = self.adopt_child(arg_expr) 38 | 39 | def execute(self, frame): 40 | if self._method is None: 41 | self._method = self._super_class.lookup_invokable(self._selector) 42 | if not self._method: 43 | raise Exception("Not yet implemented") 44 | 45 | rcvr = self._rcvr_expr.execute(frame) 46 | arg = self._arg_expr.execute(frame) 47 | return self._method.invoke_2(rcvr, arg) 48 | 49 | 50 | class TernarySuper(AbstractSuperMessageNode): 51 | _immutable_fields_ = ["_arg1_expr?", "_arg2_expr?"] 52 | _child_nodes_ = ["_arg1_expr", "_arg2_expr"] 53 | 54 | def __init__( 55 | self, 56 | selector, 57 | rcvr_expr, 58 | arg1_expr, 59 | arg2_expr, 60 | super_class, 61 | source_section=None, 62 | ): 63 | AbstractSuperMessageNode.__init__( 64 | self, selector, rcvr_expr, super_class, source_section 65 | ) 66 | self._arg1_expr = self.adopt_child(arg1_expr) 67 | self._arg2_expr = self.adopt_child(arg2_expr) 68 | 69 | def execute(self, frame): 70 | if self._method is None: 71 | self._method = self._super_class.lookup_invokable(self._selector) 72 | if not self._method: 73 | raise Exception("Not yet implemented") 74 | 75 | rcvr = self._rcvr_expr.execute(frame) 76 | arg1 = self._arg1_expr.execute(frame) 77 | arg2 = self._arg2_expr.execute(frame) 78 | return self._method.invoke_3(rcvr, arg1, arg2) 79 | 80 | 81 | class NArySuper(AbstractSuperMessageNode): 82 | _immutable_fields_ = ["_arg_exprs?[*]"] 83 | _child_nodes_ = ["_arg_exprs[*]"] 84 | 85 | def __init__( 86 | self, selector, rcvr_expr, arg_exprs, super_class, source_section=None 87 | ): 88 | AbstractSuperMessageNode.__init__( 89 | self, selector, rcvr_expr, super_class, source_section 90 | ) 91 | assert len(arg_exprs) > 2 92 | self._arg_exprs = self.adopt_children(arg_exprs) 93 | 94 | def execute(self, frame): 95 | if self._method is None: 96 | self._method = self._super_class.lookup_invokable(self._selector) 97 | if not self._method: 98 | raise Exception("Not yet implemented") 99 | 100 | rcvr = self._rcvr_expr.execute(frame) 101 | args = [arg_exp.execute(frame) for arg_exp in self._arg_exprs] 102 | return self._method.invoke_args(rcvr, args) 103 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/message/uninitialized_node.py: -------------------------------------------------------------------------------- 1 | from som.interpreter.ast.nodes.message.abstract_node import AbstractMessageNode 2 | from som.interpreter.ast.nodes.message.generic_node import ( 3 | UnarySend, 4 | BinarySend, 5 | TernarySend, 6 | NArySend, 7 | ) 8 | 9 | from som.interpreter.ast.nodes.specialized.down_to_do_node import ( 10 | IntDownToIntDoNode, 11 | IntDownToDoubleDoNode, 12 | ) 13 | from som.interpreter.ast.nodes.specialized.if_true_false import ( 14 | IfTrueIfFalseNode, 15 | IfNode, 16 | ) 17 | from som.interpreter.ast.nodes.specialized.to_by_do_node import ( 18 | IntToIntByDoNode, 19 | IntToDoubleByDoNode, 20 | ) 21 | from som.interpreter.ast.nodes.specialized.to_do_node import ( 22 | IntToIntDoNode, 23 | IntToDoubleDoNode, 24 | ) 25 | 26 | 27 | class UninitializedMessageNode(AbstractMessageNode): 28 | def execute(self, frame): 29 | rcvr, args = self._evaluate_rcvr_and_args(frame) 30 | return self._specialize(frame, rcvr, args).execute_evaluated(frame, rcvr, args) 31 | 32 | def _specialize(self, _frame, rcvr, args): 33 | if args: 34 | for specialization in [ 35 | IntToIntDoNode, 36 | IntToDoubleDoNode, 37 | IntToIntByDoNode, 38 | IntToDoubleByDoNode, 39 | IntDownToIntDoNode, 40 | IntDownToDoubleDoNode, 41 | IfTrueIfFalseNode, 42 | IfNode, 43 | ]: 44 | if specialization.can_specialize(self._selector, rcvr, args, self): 45 | return specialization.specialize_node( 46 | self._selector, rcvr, args, self 47 | ) 48 | num_args = len(args) + 1 49 | if num_args == 1: 50 | node = UnarySend( 51 | self._selector, self.universe, self._rcvr_expr, self.source_section 52 | ) 53 | elif num_args == 2: 54 | node = BinarySend( 55 | self._selector, 56 | self.universe, 57 | self._rcvr_expr, 58 | self._arg_exprs[0], 59 | self.source_section, 60 | ) 61 | elif num_args == 3: 62 | node = TernarySend( 63 | self._selector, 64 | self.universe, 65 | self._rcvr_expr, 66 | self._arg_exprs[0], 67 | self._arg_exprs[1], 68 | self.source_section, 69 | ) 70 | else: 71 | node = NArySend( 72 | self._selector, 73 | self.universe, 74 | self._rcvr_expr, 75 | self._arg_exprs, 76 | self.source_section, 77 | ) 78 | return self.replace(node) 79 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/return_non_local_node.py: -------------------------------------------------------------------------------- 1 | from som.interpreter.ast.frame import ( 2 | mark_as_no_longer_on_stack, 3 | get_inner_as_context, 4 | read_frame, 5 | FRAME_AND_INNER_RCVR_IDX, 6 | is_on_stack, 7 | ) 8 | from som.interpreter.ast.nodes.contextual_node import ContextualNode 9 | from som.interpreter.ast.nodes.expression_node import ExpressionNode 10 | 11 | from som.interpreter.control_flow import ReturnException 12 | from som.interpreter.send import lookup_and_send_2 13 | 14 | 15 | class ReturnLocalNode(ExpressionNode): 16 | _immutable_fields_ = ["_expr?", "universe"] 17 | _child_nodes_ = ["_expr"] 18 | 19 | def __init__(self, expr, universe, source_section=None): 20 | ExpressionNode.__init__(self, source_section) 21 | self._expr = self.adopt_child(expr) 22 | self.universe = universe 23 | 24 | def execute(self, frame): 25 | result = self._expr.execute(frame) 26 | inner = get_inner_as_context(frame) 27 | 28 | assert is_on_stack(inner) 29 | raise ReturnException(result, inner) 30 | 31 | 32 | class ReturnNonLocalNode(ContextualNode): 33 | _immutable_fields_ = ["_expr?", "universe"] 34 | _child_nodes_ = ["_expr"] 35 | 36 | def __init__(self, context_level, expr, universe, source_section=None): 37 | ContextualNode.__init__(self, context_level, source_section) 38 | self._expr = self.adopt_child(expr) 39 | self.universe = universe 40 | 41 | def execute(self, frame): 42 | result = self._expr.execute(frame) 43 | block = self.determine_block(frame) 44 | 45 | if block.is_outer_on_stack(): 46 | raise ReturnException(result, block.get_on_stack_marker()) 47 | outer_self = self.determine_outer_self(frame) 48 | self_block = read_frame(frame, FRAME_AND_INNER_RCVR_IDX) 49 | return lookup_and_send_2(outer_self, self_block, "escapedBlock:") 50 | 51 | def handle_inlining(self, mgenc): 52 | self._context_level -= 1 53 | if self._context_level == 0: 54 | self.replace( 55 | ReturnLocalNode(self._expr, self.universe, self.source_section) 56 | ) 57 | assert self._context_level >= 0 58 | 59 | def handle_outer_inlined(self, removed_ctx_level, mgenc_with_inlined): 60 | self._context_level -= 1 61 | if self._context_level == 0: 62 | self.replace( 63 | ReturnLocalNode(self._expr, self.universe, self.source_section) 64 | ) 65 | assert self._context_level >= 0 66 | 67 | 68 | class CatchNonLocalReturnNode(ExpressionNode): 69 | _immutable_fields_ = ["_method_body?"] 70 | _child_nodes_ = ["_method_body"] 71 | 72 | def __init__(self, method_body, source_section=None): 73 | ExpressionNode.__init__(self, source_section) 74 | self._method_body = self.adopt_child(method_body) 75 | 76 | def execute(self, frame): 77 | inner = get_inner_as_context(frame) 78 | assert isinstance(inner, list) 79 | 80 | try: 81 | return self._method_body.execute(frame) 82 | except ReturnException as ex: 83 | if not ex.has_reached_target(inner): 84 | raise ex 85 | return ex.get_result() 86 | finally: 87 | mark_as_no_longer_on_stack(inner) 88 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/sequence_node.py: -------------------------------------------------------------------------------- 1 | from rlib.jit import unroll_safe 2 | 3 | from som.interpreter.ast.nodes.expression_node import ExpressionNode 4 | from som.interpreter.ast.nodes.variable_node import LocalFrameVarReadNode 5 | 6 | 7 | class SequenceNode(ExpressionNode): 8 | _immutable_fields_ = ["_exprs?[*]"] 9 | _child_nodes_ = ["_exprs[*]"] 10 | 11 | def __init__(self, expressions, source_section): 12 | ExpressionNode.__init__(self, source_section) 13 | self._exprs = self.adopt_children(expressions) 14 | 15 | def execute(self, frame): 16 | self._execute_all_but_last(frame) 17 | return self._exprs[-1].execute(frame) 18 | 19 | @unroll_safe 20 | def _execute_all_but_last(self, frame): 21 | for i in range(0, len(self._exprs) - 1): 22 | self._exprs[i].execute(frame) 23 | 24 | def create_trivial_method(self, signature): 25 | if len(self._exprs) != 2: 26 | return None 27 | 28 | return_exp = self._exprs[1] 29 | if isinstance(return_exp, LocalFrameVarReadNode) and return_exp.is_self(): 30 | expr = self._exprs[0] 31 | if expr.is_trivial_in_sequence(): 32 | return expr.create_trivial_method(signature) 33 | 34 | return None 35 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/specialized/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOM-st/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/interpreter/ast/nodes/specialized/__init__.py -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/specialized/down_to_do_node.py: -------------------------------------------------------------------------------- 1 | from rlib import jit 2 | from som.interpreter.ast.nodes.specialized.to_do_node import AbstractToDoNode 3 | 4 | from som.vmobjects.block_ast import AstBlock 5 | from som.vmobjects.double import Double 6 | from som.vmobjects.integer import Integer 7 | from som.vmobjects.method_ast import AstMethod 8 | 9 | 10 | def get_printable_location(block_method): 11 | assert isinstance(block_method, AstMethod) 12 | return "#to:do: %s" % block_method.merge_point_string() 13 | 14 | 15 | int_driver = jit.JitDriver( 16 | greens=["block_method"], 17 | reds="auto", 18 | is_recursive=True, 19 | # virtualizables=['frame'], 20 | get_printable_location=get_printable_location, 21 | ) 22 | 23 | 24 | class IntDownToIntDoNode(AbstractToDoNode): 25 | @staticmethod 26 | def _do_loop(rcvr, limit, body_block): 27 | block_method = body_block.get_method() 28 | 29 | i = rcvr.get_embedded_integer() 30 | bottom = limit.get_embedded_integer() 31 | while i >= bottom: 32 | int_driver.jit_merge_point(block_method=block_method) 33 | block_method.invoke_2(body_block, Integer(i)) 34 | i -= 1 35 | 36 | @staticmethod 37 | def can_specialize(selector, rcvr, args, _node): 38 | return ( 39 | isinstance(args[0], Integer) 40 | and isinstance(rcvr, Integer) 41 | and len(args) > 1 42 | and isinstance(args[1], AstBlock) 43 | and selector.get_embedded_string() == "downTo:do:" 44 | ) 45 | 46 | @staticmethod 47 | def specialize_node(_selector, _rcvr, _args, node): 48 | return node.replace( 49 | IntDownToIntDoNode( 50 | node._rcvr_expr, # pylint: disable=protected-access 51 | node._arg_exprs[0], # pylint: disable=protected-access 52 | node._arg_exprs[1], # pylint: disable=protected-access 53 | node.universe, 54 | node.source_section, 55 | ) 56 | ) 57 | 58 | 59 | double_driver = jit.JitDriver( 60 | greens=["block_method"], 61 | reds="auto", 62 | is_recursive=True, 63 | # virtualizables=['frame'], 64 | get_printable_location=get_printable_location, 65 | ) 66 | 67 | 68 | class IntDownToDoubleDoNode(AbstractToDoNode): 69 | @staticmethod 70 | def _do_loop(rcvr, limit, body_block): 71 | block_method = body_block.get_method() 72 | 73 | i = rcvr.get_embedded_integer() 74 | bottom = limit.get_embedded_double() 75 | while i >= bottom: 76 | double_driver.jit_merge_point(block_method=block_method) 77 | block_method.invoke_2(body_block, Integer(i)) 78 | i -= 1 79 | 80 | @staticmethod 81 | def can_specialize(selector, rcvr, args, _node): 82 | return ( 83 | isinstance(args[0], Double) 84 | and isinstance(rcvr, Integer) 85 | and len(args) > 1 86 | and isinstance(args[1], AstBlock) 87 | and selector.get_embedded_string() == "downTo:do:" 88 | ) 89 | 90 | @staticmethod 91 | def specialize_node(_selector, _rcvr, _args, node): 92 | return node.replace( 93 | IntDownToDoubleDoNode( 94 | node._rcvr_expr, # pylint: disable=protected-access 95 | node._arg_exprs[0], # pylint: disable=protected-access 96 | node._arg_exprs[1], # pylint: disable=protected-access 97 | node.universe, 98 | node.source_section, 99 | ) 100 | ) 101 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/specialized/if_true_false.py: -------------------------------------------------------------------------------- 1 | from som.interpreter.ast.nodes.expression_node import ExpressionNode 2 | from som.vm.globals import nilObject, trueObject, falseObject 3 | from som.vmobjects.block_ast import AstBlock 4 | 5 | 6 | class IfTrueIfFalseNode(ExpressionNode): 7 | _immutable_fields_ = ["_rcvr_expr?", "_true_expr?", "_false_expr?", "universe"] 8 | _child_nodes_ = ["_rcvr_expr", "_true_expr", "_false_expr"] 9 | 10 | def __init__(self, rcvr_expr, true_expr, false_expr, universe, source_section): 11 | ExpressionNode.__init__(self, source_section) 12 | self._rcvr_expr = self.adopt_child(rcvr_expr) 13 | self._true_expr = self.adopt_child(true_expr) 14 | self._false_expr = self.adopt_child(false_expr) 15 | self.universe = universe 16 | 17 | def execute(self, frame): 18 | rcvr = self._rcvr_expr.execute(frame) 19 | true = self._true_expr.execute(frame) 20 | false = self._false_expr.execute(frame) 21 | 22 | return self._do_iftrue_iffalse(rcvr, true, false) 23 | 24 | def execute_evaluated(self, _frame, rcvr, args): 25 | return self._do_iftrue_iffalse(rcvr, args[0], args[1]) 26 | 27 | @staticmethod 28 | def _do_iftrue_iffalse(rcvr, true, false): 29 | if rcvr is trueObject: 30 | if isinstance(true, AstBlock): 31 | return true.get_method().invoke_1(true) 32 | return true 33 | 34 | assert rcvr is falseObject 35 | if isinstance(false, AstBlock): 36 | return false.get_method().invoke_1(false) 37 | return false 38 | 39 | @staticmethod 40 | def can_specialize(selector, rcvr, args, _node): 41 | return ( 42 | len(args) == 2 43 | and (rcvr is trueObject or rcvr is falseObject) 44 | and selector.get_embedded_string() == "ifTrue:ifFalse:" 45 | ) 46 | 47 | @staticmethod 48 | def specialize_node(_selector, _rcvr, _args, node): 49 | return node.replace( 50 | IfTrueIfFalseNode( 51 | node._rcvr_expr, # pylint: disable=protected-access 52 | node._arg_exprs[0], # pylint: disable=protected-access 53 | node._arg_exprs[1], # pylint: disable=protected-access 54 | node.universe, 55 | node.source_section, 56 | ) 57 | ) 58 | 59 | 60 | class IfNode(ExpressionNode): 61 | _immutable_fields_ = ["_rcvr_expr?", "_branch_expr?", "_condition", "universe"] 62 | _child_nodes_ = ["_rcvr_expr", "_branch_expr"] 63 | 64 | def __init__(self, rcvr_expr, branch_expr, condition_obj, universe, source_section): 65 | ExpressionNode.__init__(self, source_section) 66 | self._rcvr_expr = self.adopt_child(rcvr_expr) 67 | self._branch_expr = self.adopt_child(branch_expr) 68 | self._condition = condition_obj 69 | self.universe = universe 70 | 71 | def execute(self, frame): 72 | rcvr = self._rcvr_expr.execute(frame) 73 | branch = self._branch_expr.execute(frame) 74 | return self._do_if(rcvr, branch) 75 | 76 | def execute_evaluated(self, _frame, rcvr, args): 77 | return self._do_if(rcvr, args[0]) 78 | 79 | def _do_if(self, rcvr, branch): 80 | if rcvr is self._condition: 81 | if isinstance(branch, AstBlock): 82 | return branch.get_method().invoke_1(branch) 83 | return branch 84 | assert rcvr is falseObject or rcvr is trueObject 85 | return nilObject 86 | 87 | @staticmethod 88 | def can_specialize(selector, rcvr, args, _node): 89 | sel = selector.get_embedded_string() 90 | return ( 91 | len(args) == 1 92 | and (rcvr is trueObject or rcvr is falseObject) 93 | and (sel == "ifTrue:" or sel == "ifFalse:") 94 | ) 95 | 96 | @staticmethod 97 | def specialize_node(selector, _rcvr, _args, node): 98 | if selector.get_embedded_string() == "ifTrue:": 99 | return node.replace( 100 | IfNode( 101 | node._rcvr_expr, # pylint: disable=protected-access 102 | node._arg_exprs[0], # pylint: disable=protected-access 103 | trueObject, 104 | node.universe, 105 | node.source_section, 106 | ) 107 | ) 108 | assert selector.get_embedded_string() == "ifFalse:" 109 | return node.replace( 110 | IfNode( 111 | node._rcvr_expr, # pylint: disable=protected-access 112 | node._arg_exprs[0], # pylint: disable=protected-access 113 | falseObject, 114 | node.universe, 115 | node.source_section, 116 | ) 117 | ) 118 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/specialized/int_inc_node.py: -------------------------------------------------------------------------------- 1 | from som.interpreter.ast.nodes.expression_node import ExpressionNode 2 | 3 | 4 | class IntIncrementNode(ExpressionNode): 5 | _immutable_fields_ = ["_rcvr_expr?"] 6 | _child_nodes_ = ["_rcvr_expr"] 7 | 8 | def __init__(self, rcvr_expr, source_section): 9 | ExpressionNode.__init__(self, source_section) 10 | self._rcvr_expr = self.adopt_child(rcvr_expr) 11 | 12 | def execute(self, frame): 13 | result = self._rcvr_expr.execute(frame) 14 | return result.prim_inc() 15 | 16 | def does_access_field(self, field_idx): 17 | from som.interpreter.ast.nodes.field_node import FieldReadNode 18 | 19 | rcvr = self._rcvr_expr 20 | return isinstance(rcvr, FieldReadNode) and field_idx == rcvr.field_idx 21 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/specialized/literal_and_or.py: -------------------------------------------------------------------------------- 1 | from som.interpreter.ast.nodes.expression_node import ExpressionNode 2 | from som.vm.globals import trueObject, falseObject 3 | 4 | 5 | class AndInlinedNode(ExpressionNode): 6 | _immutable_fields_ = ["_rcvr_expr?", "_arg_expr?"] 7 | _child_nodes_ = ["_rcvr_expr", "_arg_expr"] 8 | 9 | def __init__(self, rcvr_expr, arg_expr, source_section): 10 | ExpressionNode.__init__(self, source_section) 11 | self._rcvr_expr = self.adopt_child(rcvr_expr) 12 | self._arg_expr = self.adopt_child(arg_expr) 13 | 14 | def execute(self, frame): 15 | result = self._rcvr_expr.execute(frame) 16 | if result is trueObject: 17 | return self._arg_expr.execute(frame) 18 | assert result is falseObject 19 | return falseObject 20 | 21 | 22 | class OrInlinedNode(ExpressionNode): 23 | _immutable_fields_ = ["_rcvr_expr?", "_arg_expr?"] 24 | _child_nodes_ = ["_rcvr_expr", "_arg_expr"] 25 | 26 | def __init__(self, rcvr_expr, arg_expr, source_section): 27 | ExpressionNode.__init__(self, source_section) 28 | self._rcvr_expr = self.adopt_child(rcvr_expr) 29 | self._arg_expr = self.adopt_child(arg_expr) 30 | 31 | def execute(self, frame): 32 | result = self._rcvr_expr.execute(frame) 33 | if result is trueObject: 34 | return trueObject 35 | assert result is falseObject 36 | return self._arg_expr.execute(frame) 37 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/specialized/literal_if.py: -------------------------------------------------------------------------------- 1 | from som.interpreter.ast.nodes.expression_node import ExpressionNode 2 | from som.vm.globals import trueObject, falseObject, nilObject 3 | 4 | 5 | class IfInlinedNode(ExpressionNode): 6 | _immutable_fields_ = [ 7 | "_condition_expr?", 8 | "_body_expr?", 9 | "universe", 10 | "_expected_bool", 11 | "_not_expected_bool", 12 | ] 13 | _child_nodes_ = ["_condition_expr", "_body_expr"] 14 | 15 | def __init__(self, condition_expr, body_expr, if_true, source_section): 16 | ExpressionNode.__init__(self, source_section) 17 | self._condition_expr = self.adopt_child(condition_expr) 18 | self._body_expr = self.adopt_child(body_expr) 19 | self._expected_bool = trueObject if if_true else falseObject 20 | self._not_expected_bool = falseObject if if_true else trueObject 21 | 22 | def execute(self, frame): 23 | result = self._condition_expr.execute(frame) 24 | if self._expected_bool is result: 25 | return self._body_expr.execute(frame) 26 | if result is self._not_expected_bool: 27 | return nilObject 28 | raise NotImplementedError( 29 | "Would need to generalize, but we haven't implemented that " 30 | + "for the bytecode interpreter either" 31 | ) 32 | 33 | 34 | class IfNilInlinedNode(ExpressionNode): 35 | _immutable_fields_ = [ 36 | "_condition_expr?", 37 | "_body_expr?", 38 | "universe", 39 | ] 40 | _child_nodes_ = ["_condition_expr", "_body_expr"] 41 | 42 | def __init__(self, condition_expr, body_expr, source_section): 43 | ExpressionNode.__init__(self, source_section) 44 | self._condition_expr = self.adopt_child(condition_expr) 45 | self._body_expr = self.adopt_child(body_expr) 46 | 47 | def execute(self, frame): 48 | result = self._condition_expr.execute(frame) 49 | if result is nilObject: 50 | return self._body_expr.execute(frame) 51 | return result 52 | 53 | 54 | class IfNotNilInlinedNode(ExpressionNode): 55 | _immutable_fields_ = [ 56 | "_condition_expr?", 57 | "_body_expr?", 58 | "universe", 59 | ] 60 | _child_nodes_ = ["_condition_expr", "_body_expr"] 61 | 62 | def __init__(self, condition_expr, body_expr, source_section): 63 | ExpressionNode.__init__(self, source_section) 64 | self._condition_expr = self.adopt_child(condition_expr) 65 | self._body_expr = self.adopt_child(body_expr) 66 | 67 | def execute(self, frame): 68 | result = self._condition_expr.execute(frame) 69 | if result is nilObject: 70 | return result 71 | return self._body_expr.execute(frame) 72 | 73 | 74 | class IfElseInlinedNode(ExpressionNode): 75 | _immutable_fields_ = [ 76 | "_condition_expr?", 77 | "_true_expr?", 78 | "_false_expr?", 79 | "universe", 80 | "_expected_bool", 81 | "_not_expected_bool", 82 | ] 83 | _child_nodes_ = ["_condition_expr", "_true_expr", "_false_expr"] 84 | 85 | def __init__(self, condition_expr, true_expr, false_expr, if_true, source_section): 86 | ExpressionNode.__init__(self, source_section) 87 | self._condition_expr = self.adopt_child(condition_expr) 88 | self._true_expr = self.adopt_child(true_expr) 89 | self._false_expr = self.adopt_child(false_expr) 90 | 91 | self._expected_bool = trueObject if if_true else falseObject 92 | self._not_expected_bool = falseObject if if_true else trueObject 93 | 94 | def execute(self, frame): 95 | result = self._condition_expr.execute(frame) 96 | if self._expected_bool is result: 97 | return self._true_expr.execute(frame) 98 | if result is self._not_expected_bool: 99 | return self._false_expr.execute(frame) 100 | raise NotImplementedError( 101 | "Would need to generalize, but we haven't implemented that " 102 | + "for the bytecode interpreter either" 103 | ) 104 | 105 | 106 | class IfNilNotNilInlinedNode(ExpressionNode): 107 | _immutable_fields_ = [ 108 | "_condition_expr?", 109 | "_nil_expr?", 110 | "_not_nil_expr?", 111 | "universe", 112 | ] 113 | _child_nodes_ = ["_condition_expr", "_nil_expr", "_not_nil_expr"] 114 | 115 | def __init__(self, condition_expr, nil_expr, not_nil_expr, source_section): 116 | ExpressionNode.__init__(self, source_section) 117 | self._condition_expr = self.adopt_child(condition_expr) 118 | self._nil_expr = self.adopt_child(nil_expr) 119 | self._not_nil_expr = self.adopt_child(not_nil_expr) 120 | 121 | def execute(self, frame): 122 | result = self._condition_expr.execute(frame) 123 | if result is nilObject: 124 | return self._nil_expr.execute(frame) 125 | return self._not_nil_expr.execute(frame) 126 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/specialized/literal_while.py: -------------------------------------------------------------------------------- 1 | from rlib.jit import JitDriver 2 | 3 | from som.interpreter.ast.nodes.expression_node import ExpressionNode 4 | from som.vm.globals import trueObject, falseObject, nilObject 5 | 6 | 7 | def get_printable_location_while(self): 8 | assert isinstance(self, WhileInlinedNode) 9 | 10 | return "while " + str(self.source_section) 11 | 12 | 13 | while_driver = JitDriver( 14 | greens=["self"], 15 | reds=["frame"], 16 | is_recursive=True, 17 | get_printable_location=get_printable_location_while, 18 | ) 19 | 20 | 21 | class WhileInlinedNode(ExpressionNode): 22 | _immutable_fields_ = [ 23 | "_condition_expr?", 24 | "_body_expr?", 25 | "_expected_bool", 26 | "_not_expected_bool", 27 | ] 28 | _child_nodes_ = ["_condition_expr", "_body_expr"] 29 | 30 | def __init__(self, condition_expr, body_expr, while_true, source_section): 31 | ExpressionNode.__init__(self, source_section) 32 | self._condition_expr = self.adopt_child(condition_expr) 33 | self._body_expr = self.adopt_child(body_expr) 34 | self._expected_bool = trueObject if while_true else falseObject 35 | self._not_expected_bool = falseObject if while_true else trueObject 36 | 37 | def execute(self, frame): 38 | while True: 39 | while_driver.jit_merge_point(self=self, frame=frame) 40 | 41 | cond = self._condition_expr.execute(frame) 42 | if cond is self._expected_bool: 43 | self._body_expr.execute(frame) 44 | elif cond is self._not_expected_bool: 45 | return nilObject 46 | else: 47 | raise NotImplementedError( 48 | "Would need to generalize, but we haven't implemented that " 49 | + "for the bytecode interpreter either" 50 | ) 51 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/specialized/to_by_do_node.py: -------------------------------------------------------------------------------- 1 | from rlib import jit 2 | 3 | from som.interpreter.ast.nodes.specialized.to_do_node import AbstractToDoNode 4 | 5 | from som.vmobjects.block_ast import AstBlock 6 | from som.vmobjects.double import Double 7 | from som.vmobjects.integer import Integer 8 | from som.vmobjects.method_ast import AstMethod 9 | 10 | 11 | class AbstractToByDoNode(AbstractToDoNode): 12 | _immutable_fields_ = ["_step_expr?"] 13 | _child_nodes_ = ["_step_expr"] 14 | 15 | def __init__( 16 | self, rcvr_expr, limit_expr, step_expr, body_expr, universe, source_section=None 17 | ): 18 | AbstractToDoNode.__init__( 19 | self, rcvr_expr, limit_expr, body_expr, universe, source_section 20 | ) 21 | self._step_expr = self.adopt_child(step_expr) 22 | 23 | def execute(self, frame): 24 | rcvr = self._rcvr_expr.execute(frame) 25 | limit = self._limit_expr.execute(frame) 26 | step = self._step_expr.execute(frame) 27 | body = self._body_expr.execute(frame) 28 | self._to_by_loop(rcvr, limit, step, body) 29 | return rcvr 30 | 31 | def execute_evaluated(self, _frame, rcvr, args): 32 | self._to_by_loop(rcvr, args[0], args[1], args[2]) 33 | return rcvr 34 | 35 | @staticmethod 36 | def _to_by_loop(_rcvr, _limit, _step, _body): 37 | raise Exception("Implemented in Subclass") 38 | 39 | 40 | def get_printable_location(block_method): 41 | assert isinstance(block_method, AstMethod) 42 | return "#to:do: %s" % block_method.merge_point_string() 43 | 44 | 45 | int_driver = jit.JitDriver( 46 | greens=["block_method"], 47 | reds="auto", 48 | is_recursive=True, 49 | # virtualizables=['frame'], 50 | get_printable_location=get_printable_location, 51 | ) 52 | 53 | 54 | class IntToIntByDoNode(AbstractToByDoNode): 55 | @staticmethod 56 | def _to_by_loop(rcvr, limit, step, body_block): 57 | block_method = body_block.get_method() 58 | 59 | i = rcvr.get_embedded_integer() 60 | top = limit.get_embedded_integer() 61 | by = step.get_embedded_integer() 62 | while i <= top: 63 | int_driver.jit_merge_point(block_method=block_method) 64 | block_method.invoke_2(body_block, Integer(i)) 65 | i += by 66 | 67 | @staticmethod 68 | def can_specialize(selector, rcvr, args, _node): 69 | return ( 70 | isinstance(args[0], Integer) 71 | and isinstance(rcvr, Integer) 72 | and len(args) == 3 73 | and isinstance(args[1], Integer) 74 | and isinstance(args[2], AstBlock) 75 | and selector.get_embedded_string() == "to:by:do:" 76 | ) 77 | 78 | @staticmethod 79 | def specialize_node(_selector, _rcvr, _args, node): 80 | return node.replace( 81 | IntToIntByDoNode( 82 | node._rcvr_expr, # pylint: disable=protected-access 83 | node._arg_exprs[0], # pylint: disable=protected-access 84 | node._arg_exprs[1], # pylint: disable=protected-access 85 | node._arg_exprs[2], # pylint: disable=protected-access 86 | node.universe, 87 | node.source_section, 88 | ) 89 | ) 90 | 91 | 92 | double_driver = jit.JitDriver( 93 | greens=["block_method"], 94 | reds="auto", 95 | is_recursive=True, 96 | # virtualizables=['frame'], 97 | get_printable_location=get_printable_location, 98 | ) 99 | 100 | 101 | class IntToDoubleByDoNode(AbstractToByDoNode): 102 | @staticmethod 103 | def _to_by_loop(rcvr, limit, step, body_block): 104 | block_method = body_block.get_method() 105 | 106 | i = rcvr.get_embedded_integer() 107 | top = limit.get_embedded_double() 108 | by = step.get_embedded_integer() 109 | while i <= top: 110 | double_driver.jit_merge_point(block_method=block_method) 111 | block_method.invoke_2(body_block, Integer(i)) 112 | i += by 113 | 114 | @staticmethod 115 | def can_specialize(selector, rcvr, args, _node): 116 | return ( 117 | isinstance(args[0], Double) 118 | and isinstance(rcvr, Integer) 119 | and len(args) == 3 120 | and isinstance(args[1], Integer) 121 | and isinstance(args[2], AstBlock) 122 | and selector.get_embedded_string() == "to:by:do:" 123 | ) 124 | 125 | @staticmethod 126 | def specialize_node(_selector, _rcvr, _args, node): 127 | return node.replace( 128 | IntToDoubleByDoNode( 129 | node._rcvr_expr, # pylint: disable=protected-access 130 | node._arg_exprs[0], # pylint: disable=protected-access 131 | node._arg_exprs[1], # pylint: disable=protected-access 132 | node._arg_exprs[2], # pylint: disable=protected-access 133 | node.universe, 134 | node.source_section, 135 | ) 136 | ) 137 | -------------------------------------------------------------------------------- /src/som/interpreter/ast/nodes/specialized/to_do_node.py: -------------------------------------------------------------------------------- 1 | from rlib import jit 2 | 3 | from som.interpreter.ast.nodes.expression_node import ExpressionNode 4 | 5 | from som.vmobjects.block_ast import AstBlock 6 | from som.vmobjects.double import Double 7 | from som.vmobjects.integer import Integer 8 | from som.vmobjects.method_ast import AstMethod 9 | 10 | 11 | class AbstractToDoNode(ExpressionNode): 12 | _immutable_fields_ = ["_rcvr_expr?", "_limit_expr?", "_body_expr?", "universe"] 13 | _child_nodes_ = ["_rcvr_expr", "_limit_expr", "_body_expr"] 14 | 15 | def __init__(self, rcvr_expr, limit_expr, body_expr, universe, source_section=None): 16 | ExpressionNode.__init__(self, source_section) 17 | self._rcvr_expr = self.adopt_child(rcvr_expr) 18 | self._limit_expr = self.adopt_child(limit_expr) 19 | self._body_expr = self.adopt_child(body_expr) 20 | self.universe = universe 21 | 22 | def execute(self, frame): 23 | rcvr = self._rcvr_expr.execute(frame) 24 | limit = self._limit_expr.execute(frame) 25 | body = self._body_expr.execute(frame) 26 | self._do_loop(rcvr, limit, body) 27 | return rcvr 28 | 29 | def execute_evaluated(self, _frame, rcvr, args): 30 | self._do_loop(rcvr, args[0], args[1]) 31 | return rcvr 32 | 33 | @staticmethod 34 | def _do_loop(_rcvr, _a1, _a2): # pylint: disable=W 35 | raise Exception("Implemented in Subclass") 36 | 37 | 38 | def get_printable_location(block_method): 39 | assert isinstance(block_method, AstMethod) 40 | return "#to:do: %s" % block_method.merge_point_string() 41 | 42 | 43 | int_driver = jit.JitDriver( 44 | greens=["block_method"], 45 | reds="auto", 46 | is_recursive=True, 47 | # virtualizables=['frame'], 48 | get_printable_location=get_printable_location, 49 | ) 50 | 51 | 52 | class IntToIntDoNode(AbstractToDoNode): 53 | @staticmethod 54 | def _do_loop(rcvr, limit, body_block): 55 | block_method = body_block.get_method() 56 | 57 | i = rcvr.get_embedded_integer() 58 | top = limit.get_embedded_integer() 59 | while i <= top: 60 | int_driver.jit_merge_point(block_method=block_method) 61 | block_method.invoke_2(body_block, Integer(i)) 62 | i += 1 63 | 64 | @staticmethod 65 | def can_specialize(selector, rcvr, args, _node): 66 | return ( 67 | isinstance(args[0], Integer) 68 | and isinstance(rcvr, Integer) 69 | and len(args) > 1 70 | and isinstance(args[1], AstBlock) 71 | and selector.get_embedded_string() == "to:do:" 72 | ) 73 | 74 | @staticmethod 75 | def specialize_node(_selector, _rcvr, _args, node): 76 | return node.replace( 77 | IntToIntDoNode( 78 | node._rcvr_expr, # pylint: disable=protected-access 79 | node._arg_exprs[0], # pylint: disable=protected-access 80 | node._arg_exprs[1], # pylint: disable=protected-access 81 | node.universe, 82 | node.source_section, 83 | ) 84 | ) 85 | 86 | 87 | double_driver = jit.JitDriver( 88 | greens=["block_method"], 89 | reds="auto", 90 | is_recursive=True, 91 | # virtualizables=['frame'], 92 | get_printable_location=get_printable_location, 93 | ) 94 | 95 | 96 | class IntToDoubleDoNode(AbstractToDoNode): 97 | @staticmethod 98 | def _do_loop(rcvr, limit, body_block): 99 | block_method = body_block.get_method() 100 | 101 | i = rcvr.get_embedded_integer() 102 | top = limit.get_embedded_double() 103 | while i <= top: 104 | double_driver.jit_merge_point(block_method=block_method) 105 | block_method.invoke_2(body_block, Integer(i)) 106 | i += 1 107 | 108 | @staticmethod 109 | def can_specialize(selector, rcvr, args, _node): 110 | return ( 111 | isinstance(args[0], Double) 112 | and isinstance(rcvr, Integer) 113 | and len(args) > 1 114 | and isinstance(args[1], AstBlock) 115 | and selector.get_embedded_string() == "to:do:" 116 | ) 117 | 118 | @staticmethod 119 | def specialize_node(_selector, _rcvr, _args, node): 120 | return node.replace( 121 | IntToDoubleDoNode( 122 | node._rcvr_expr, # pylint: disable=protected-access 123 | node._arg_exprs[0], # pylint: disable=protected-access 124 | node._arg_exprs[1], # pylint: disable=protected-access 125 | node.universe, 126 | node.source_section, 127 | ) 128 | ) 129 | -------------------------------------------------------------------------------- /src/som/interpreter/bc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOM-st/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/interpreter/bc/__init__.py -------------------------------------------------------------------------------- /src/som/interpreter/bc/frame.py: -------------------------------------------------------------------------------- 1 | from rlib import jit 2 | from rlib.debug import make_sure_not_resized 3 | 4 | from som.vm.globals import nilObject, trueObject 5 | from som.interpreter.ast.frame import ( 6 | _erase_obj, 7 | _erase_list, 8 | FRAME_AND_INNER_RCVR_IDX, 9 | _FRAME_INNER_IDX, 10 | _INNER_ON_STACK_IDX, 11 | _FRAME_AND_INNER_FIRST_ARG, 12 | read_frame, 13 | ) 14 | 15 | # Frame Design Notes 16 | # 17 | # The design of the frame for the bytecode-based interpreters is pretty much the 18 | # same as the design for the AST-based interpreter. 19 | # 20 | # Frame 21 | # 22 | # +-----------+ 23 | # | Inner | (Optional: for args/vars accessed from inner scopes, and non-local returns) 24 | # +-----------+ 25 | # | Receiver | 26 | # +-----------+ 27 | # | Arg 1 | 28 | # | ... | 29 | # | Arg n | 30 | # +-----------+ 31 | # | Local 1 | 32 | # | ... | 33 | # | Local n | 34 | # +-----------+ 35 | # 36 | # Inner 37 | # 38 | # +-----------------+ 39 | # | OnStack | boolean indicating whether the frame is still on the stack 40 | # +-----------------+ 41 | # | Receiver | the same as the receiver in the frame, not to be changed 42 | # +-----------------+ 43 | # | ArgForInner 1 | 44 | # | ... | 45 | # | ArgForInner n | 46 | # +-----------------+ 47 | # | LocalForInner 1 | 48 | # | ... | 49 | # | LocalForInner n | 50 | # +-----------------+ 51 | 52 | 53 | def create_frame( 54 | arg_inner_access_reversed, 55 | size_frame, 56 | size_inner, 57 | prev_stack, 58 | prev_stack_ptr, 59 | num_args, 60 | ): 61 | frame = [_erase_obj(nilObject)] * size_frame 62 | make_sure_not_resized(frame) 63 | 64 | receiver = prev_stack[prev_stack_ptr - (num_args - 1)] 65 | assert num_args - 1 == len(arg_inner_access_reversed) # num_args without rcvr 66 | 67 | if size_inner > 0: 68 | inner = [nilObject] * size_inner 69 | make_sure_not_resized(inner) 70 | frame[FRAME_AND_INNER_RCVR_IDX] = _erase_obj(receiver) 71 | frame[_FRAME_INNER_IDX] = _erase_list(inner) 72 | 73 | inner[_INNER_ON_STACK_IDX] = trueObject 74 | inner[FRAME_AND_INNER_RCVR_IDX] = receiver 75 | _set_arguments_with_inner( 76 | frame, 77 | inner, 78 | arg_inner_access_reversed, 79 | prev_stack, 80 | prev_stack_ptr, 81 | num_args - 1, 82 | ) 83 | else: 84 | frame[0] = _erase_list(None) 85 | frame[FRAME_AND_INNER_RCVR_IDX] = _erase_obj(receiver) 86 | _set_arguments_without_inner(frame, prev_stack, prev_stack_ptr, num_args - 1) 87 | 88 | return frame 89 | 90 | 91 | def create_frame_3( 92 | arg_inner_access_reversed, 93 | size_frame, 94 | size_inner, 95 | receiver, 96 | arg1, 97 | arg2, 98 | ): 99 | frame = [_erase_obj(nilObject)] * size_frame 100 | make_sure_not_resized(frame) 101 | 102 | if size_inner > 0: 103 | inner = [nilObject] * size_inner 104 | make_sure_not_resized(inner) 105 | frame[FRAME_AND_INNER_RCVR_IDX] = _erase_obj(receiver) 106 | frame[_FRAME_INNER_IDX] = _erase_list(inner) 107 | 108 | inner[_INNER_ON_STACK_IDX] = trueObject 109 | inner[FRAME_AND_INNER_RCVR_IDX] = receiver 110 | if arg_inner_access_reversed[1]: 111 | inner[FRAME_AND_INNER_RCVR_IDX + 1] = arg1 112 | if arg_inner_access_reversed[0]: 113 | inner[FRAME_AND_INNER_RCVR_IDX + 2] = arg2 114 | else: 115 | frame[FRAME_AND_INNER_RCVR_IDX + 1] = _erase_obj(arg2) 116 | else: 117 | frame[FRAME_AND_INNER_RCVR_IDX + 1] = _erase_obj(arg1) 118 | if arg_inner_access_reversed[0]: 119 | inner[FRAME_AND_INNER_RCVR_IDX + 1] = arg2 120 | else: 121 | frame[FRAME_AND_INNER_RCVR_IDX + 2] = _erase_obj(arg2) 122 | else: 123 | frame[0] = _erase_list(None) 124 | frame[FRAME_AND_INNER_RCVR_IDX] = _erase_obj(receiver) 125 | frame[FRAME_AND_INNER_RCVR_IDX + 1] = _erase_obj(arg1) 126 | frame[FRAME_AND_INNER_RCVR_IDX + 2] = _erase_obj(arg2) 127 | 128 | return frame 129 | 130 | 131 | @jit.unroll_safe 132 | def _set_arguments_without_inner( 133 | frame, prev_stack, prev_stack_ptr, num_args_without_rcvr 134 | ): 135 | arg_i = num_args_without_rcvr - 1 136 | frame_i = _FRAME_AND_INNER_FIRST_ARG 137 | 138 | while arg_i >= 0: 139 | frame[frame_i] = _erase_obj(prev_stack[prev_stack_ptr - arg_i]) 140 | frame_i += 1 141 | arg_i -= 1 142 | 143 | 144 | @jit.unroll_safe 145 | def _set_arguments_with_inner( 146 | frame, 147 | inner, 148 | arg_inner_access_reversed, 149 | prev_stack, 150 | prev_stack_ptr, 151 | num_args_without_rcvr, 152 | ): 153 | arg_i = num_args_without_rcvr - 1 154 | frame_i = 2 155 | inner_i = _FRAME_AND_INNER_FIRST_ARG 156 | 157 | while arg_i >= 0: 158 | arg_val = prev_stack[prev_stack_ptr - arg_i] 159 | if arg_inner_access_reversed[arg_i]: 160 | inner[inner_i] = arg_val 161 | inner_i += 1 162 | else: 163 | frame[frame_i] = _erase_obj(arg_val) 164 | frame_i += 1 165 | arg_i -= 1 166 | 167 | 168 | @jit.unroll_safe 169 | def stack_pop_old_arguments_and_push_result(stack, stack_ptr, num_args, result): 170 | for _ in range(num_args): 171 | stack[stack_ptr] = None 172 | stack_ptr -= 1 173 | stack_ptr += 1 174 | stack[stack_ptr] = result 175 | return stack_ptr 176 | 177 | 178 | @jit.unroll_safe 179 | def get_block_at(frame, ctx_level): 180 | assert ctx_level > 0 181 | 182 | block = read_frame(frame, FRAME_AND_INNER_RCVR_IDX) 183 | for _ in range(0, ctx_level - 1): 184 | block = block.get_from_outer(FRAME_AND_INNER_RCVR_IDX) 185 | return block 186 | 187 | 188 | @jit.unroll_safe 189 | def get_self_dynamically(frame): 190 | from som.vmobjects.block_bc import BcBlock 191 | from som.vmobjects.abstract_object import AbstractObject 192 | 193 | rcvr = read_frame(frame, FRAME_AND_INNER_RCVR_IDX) 194 | assert isinstance(rcvr, AbstractObject) 195 | 196 | while isinstance(rcvr, BcBlock): 197 | rcvr = rcvr.get_from_outer(FRAME_AND_INNER_RCVR_IDX) 198 | assert isinstance(rcvr, AbstractObject) 199 | 200 | return rcvr 201 | -------------------------------------------------------------------------------- /src/som/interpreter/control_flow.py: -------------------------------------------------------------------------------- 1 | class ReturnException(Exception): 2 | _immutable_fields_ = ["_result", "_target"] 3 | 4 | def __init__(self, result, target): # pylint: disable=super-init-not-called 5 | self._result = result 6 | self._target = target 7 | assert isinstance(target, list) 8 | 9 | def get_result(self): 10 | return self._result 11 | 12 | def has_reached_target(self, current): 13 | assert isinstance(self._target, list) 14 | assert isinstance(current, list) 15 | return current is self._target 16 | 17 | def __str__(self): 18 | return "ReturnEx(%s)" % self._result 19 | -------------------------------------------------------------------------------- /src/som/interpreter/objectstorage/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = "smarr" 2 | -------------------------------------------------------------------------------- /src/som/interpreter/objectstorage/layout_transitions.py: -------------------------------------------------------------------------------- 1 | class GeneralizeStorageLocationException(Exception): 2 | pass 3 | 4 | 5 | class UninitializedStorageLocationException(Exception): 6 | pass 7 | -------------------------------------------------------------------------------- /src/som/interpreter/objectstorage/object_layout.py: -------------------------------------------------------------------------------- 1 | from som.interpreter.objectstorage.storage_location import ( 2 | NUMBER_OF_POINTER_FIELDS, 3 | NUMBER_OF_PRIMITIVE_FIELDS, 4 | create_location_for_long, 5 | create_location_for_double, 6 | create_location_for_object, 7 | create_location_for_unwritten, 8 | ) 9 | from som.vmobjects.double import Double 10 | from som.vmobjects.integer import Integer 11 | 12 | from rlib.jit import elidable_promote 13 | 14 | 15 | class ObjectLayout(object): 16 | _immutable_fields_ = [ 17 | "for_class", 18 | "_prim_locations_used", 19 | "_ptr_locations_used", 20 | "_total_locations", 21 | "_storage_locations[*]", 22 | "_storage_type[*]", 23 | "is_latest?", 24 | ] 25 | 26 | def __init__(self, number_of_fields, for_class=None, known_types=None): 27 | assert number_of_fields >= 0 28 | from som.vmobjects.object_with_layout import Object 29 | 30 | self.is_latest = True 31 | self.for_class = for_class 32 | self._storage_types = known_types or [None] * number_of_fields 33 | self._total_locations = number_of_fields 34 | self._storage_locations = [None] * number_of_fields 35 | 36 | next_free_prim_idx = 0 37 | next_free_ptr_idx = 0 38 | 39 | for i in range(0, number_of_fields): 40 | storage_type = self._storage_types[i] 41 | 42 | if storage_type is Integer: 43 | location = create_location_for_long(i, next_free_prim_idx) 44 | next_free_prim_idx += 1 45 | elif storage_type is Double: 46 | location = create_location_for_double(i, next_free_prim_idx) 47 | next_free_prim_idx += 1 48 | elif storage_type is Object: 49 | location = create_location_for_object(i, next_free_ptr_idx) 50 | next_free_ptr_idx += 1 51 | else: 52 | assert storage_type is None 53 | location = create_location_for_unwritten(i) 54 | 55 | self._storage_locations[i] = location 56 | 57 | self._prim_locations_used = next_free_prim_idx 58 | self._ptr_locations_used = next_free_ptr_idx 59 | 60 | def get_number_of_fields(self): 61 | return self._total_locations 62 | 63 | def with_generalized_field(self, field_idx): 64 | from som.vmobjects.object_with_layout import Object 65 | 66 | if self._storage_types[field_idx] is Object: 67 | return self 68 | 69 | self.is_latest = False 70 | 71 | assert self._storage_types[field_idx] is not None 72 | with_generalized_field = self._storage_types[:] 73 | with_generalized_field[field_idx] = Object 74 | return ObjectLayout( 75 | self._total_locations, self.for_class, with_generalized_field 76 | ) 77 | 78 | def with_initialized_field(self, field_idx, spec_class): 79 | from som.vmobjects.object_with_layout import Object 80 | 81 | # First we generalize to Integer, Double, or Object 82 | # don't need more precision 83 | if spec_class is Integer or spec_class is Double: 84 | spec_type = spec_class 85 | else: 86 | spec_type = Object 87 | 88 | if self._storage_types[field_idx] is spec_type: 89 | return self 90 | 91 | self.is_latest = False 92 | 93 | assert self._storage_types[field_idx] is None 94 | with_initialized_field = self._storage_types[:] 95 | with_initialized_field[field_idx] = spec_type 96 | return ObjectLayout( 97 | self._total_locations, self.for_class, with_initialized_field 98 | ) 99 | 100 | def get_storage_location(self, field_idx): 101 | return self._storage_locations[field_idx] 102 | 103 | def create_access_node(self, field_idx, next_entry): 104 | return self._storage_locations[field_idx].create_access_node(self, next_entry) 105 | 106 | def get_number_of_used_extended_ptr_locations(self): 107 | required_ext_fields = self._ptr_locations_used - NUMBER_OF_POINTER_FIELDS 108 | if required_ext_fields < 0: 109 | return 0 110 | return required_ext_fields 111 | 112 | def get_number_of_used_extended_prim_locations(self): 113 | required_ext_field = self._prim_locations_used - NUMBER_OF_PRIMITIVE_FIELDS 114 | if required_ext_field < 0: 115 | return 0 116 | return required_ext_field 117 | 118 | @elidable_promote("all") 119 | def lookup_invokable(self, signature): 120 | return self.for_class.lookup_invokable(signature) 121 | -------------------------------------------------------------------------------- /src/som/interpreter/send.py: -------------------------------------------------------------------------------- 1 | from som.vm.symbols import symbol_for 2 | 3 | 4 | def lookup_and_send_2(receiver, arg, selector_string): 5 | from som.vm.current import current_universe 6 | 7 | selector = symbol_for(selector_string) 8 | invokable = receiver.get_class(current_universe).lookup_invokable(selector) 9 | 10 | return invokable.invoke_2(receiver, arg) 11 | 12 | 13 | def lookup_and_send_3(receiver, arg1, arg2, selector_string): 14 | from som.vm.current import current_universe 15 | 16 | selector = symbol_for(selector_string) 17 | invokable = receiver.get_class(current_universe).lookup_invokable(selector) 18 | return invokable.invoke_3(receiver, arg1, arg2) 19 | -------------------------------------------------------------------------------- /src/som/primitives/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOM-st/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/primitives/__init__.py -------------------------------------------------------------------------------- /src/som/primitives/array_primitives.py: -------------------------------------------------------------------------------- 1 | from rlib.jit import JitDriver 2 | 3 | from som.interp_type import is_ast_interpreter 4 | from som.vmobjects.array import Array 5 | from som.vmobjects.primitive import UnaryPrimitive, BinaryPrimitive, TernaryPrimitive 6 | from som.vmobjects.method import AbstractMethod 7 | from som.primitives.primitives import Primitives 8 | 9 | 10 | if is_ast_interpreter(): 11 | from som.vmobjects.block_ast import AstBlock as _Block 12 | else: 13 | from som.vmobjects.block_bc import BcBlock as _Block 14 | 15 | 16 | def _at(rcvr, i): 17 | return rcvr.get_indexable_field(i.get_embedded_integer() - 1) 18 | 19 | 20 | def _at_put(rcvr, index, value): 21 | rcvr.set_indexable_field(index.get_embedded_integer() - 1, value) 22 | return rcvr 23 | 24 | 25 | def _length(rcvr): 26 | from som.vmobjects.integer import Integer 27 | 28 | return Integer(rcvr.get_number_of_indexable_fields()) 29 | 30 | 31 | def _copy(rcvr): 32 | return rcvr.copy() 33 | 34 | 35 | def _new(_rcvr, length): 36 | return Array.from_size(length.get_embedded_integer()) 37 | 38 | 39 | def get_do_index_printable_location(block_method): 40 | assert isinstance(block_method, AbstractMethod) 41 | return "#doIndexes: %s" % block_method.merge_point_string() 42 | 43 | 44 | do_index_driver = JitDriver( 45 | greens=["block_method"], 46 | reds="auto", 47 | is_recursive=True, 48 | get_printable_location=get_do_index_printable_location, 49 | ) 50 | 51 | 52 | def _do_indexes(rcvr, block): 53 | from som.vmobjects.integer import Integer 54 | 55 | block_method = block.get_method() 56 | 57 | i = 1 58 | length = rcvr.get_number_of_indexable_fields() 59 | while i <= length: # the i is propagated to Smalltalk, so, start with 1 60 | do_index_driver.jit_merge_point(block_method=block_method) 61 | block_method.invoke_2(block, Integer(i)) 62 | i += 1 63 | 64 | 65 | def get_do_printable_location(block_method): 66 | assert isinstance(block_method, AbstractMethod) 67 | return "#do: %s" % block_method.merge_point_string() 68 | 69 | 70 | do_driver = JitDriver( 71 | greens=["block_method"], 72 | reds="auto", 73 | get_printable_location=get_do_printable_location, 74 | ) 75 | 76 | 77 | def _do(rcvr, block): 78 | block_method = block.get_method() 79 | 80 | i = 0 81 | length = rcvr.get_number_of_indexable_fields() 82 | while i < length: # the array itself is zero indexed 83 | do_driver.jit_merge_point(block_method=block_method) 84 | block_method.invoke_2(block, rcvr.get_indexable_field(i)) 85 | i += 1 86 | 87 | 88 | def _put_all(rcvr, arg): 89 | if isinstance(arg, _Block): 90 | rcvr.set_all_with_block(arg) 91 | return rcvr 92 | 93 | # It is a simple value, just put it into the array 94 | rcvr.set_all(arg) 95 | return rcvr 96 | 97 | 98 | class ArrayPrimitivesBase(Primitives): 99 | def install_primitives(self): 100 | self._install_instance_primitive(BinaryPrimitive("at:", _at)) 101 | self._install_instance_primitive(TernaryPrimitive("at:put:", _at_put)) 102 | self._install_instance_primitive(UnaryPrimitive("length", _length)) 103 | self._install_instance_primitive(UnaryPrimitive("copy", _copy)) 104 | 105 | self._install_class_primitive(BinaryPrimitive("new:", _new)) 106 | 107 | self._install_instance_primitive(BinaryPrimitive("doIndexes:", _do_indexes)) 108 | self._install_instance_primitive(BinaryPrimitive("do:", _do)) 109 | self._install_instance_primitive(BinaryPrimitive("putAll:", _put_all)) 110 | -------------------------------------------------------------------------------- /src/som/primitives/ast/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOM-st/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/primitives/ast/__init__.py -------------------------------------------------------------------------------- /src/som/primitives/ast/array_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.array_primitives import ArrayPrimitivesBase as _Base 2 | 3 | 4 | ArrayPrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/ast/block_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.block_primitives import BlockPrimitivesBase as _Base 2 | 3 | 4 | BlockPrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/ast/class_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.class_primitives import ClassPrimitives as _Base 2 | 3 | 4 | ClassPrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/ast/double_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.double_primitives import DoublePrimitives as _Base 2 | 3 | 4 | DoublePrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/ast/false_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.false_primitives import FalsePrimitivesBase as _Base 2 | 3 | 4 | FalsePrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/ast/integer_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.integer_primitives import IntegerPrimitivesBase as _Base 2 | 3 | 4 | IntegerPrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/ast/method_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.invokable_primitives import InvokablePrimitivesBase as _Base 2 | from som.vm.globals import nilObject 3 | from som.vmobjects.abstract_object import AbstractObject 4 | from som.vmobjects.array import Array 5 | from som.vmobjects.method_ast import AstMethod 6 | from som.vmobjects.primitive import Primitive 7 | 8 | 9 | def _invoke_on_with(_ivkbl, rcvr, args): 10 | assert isinstance(rcvr, AstMethod) 11 | assert isinstance(args[0], AbstractObject) 12 | assert isinstance(args[1], Array) or args[1] is nilObject 13 | 14 | if args[1] is nilObject: 15 | direct_args = [] 16 | else: 17 | direct_args = args[1].as_argument_array() 18 | return rcvr.invoke_args(args[0], direct_args) 19 | 20 | 21 | class MethodPrimitives(_Base): 22 | def install_primitives(self): 23 | _Base.install_primitives(self) 24 | self._install_instance_primitive(Primitive("invokeOn:with:", _invoke_on_with)) 25 | -------------------------------------------------------------------------------- /src/som/primitives/ast/object_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.object_primitives import ObjectPrimitivesBase as _Base 2 | 3 | 4 | ObjectPrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/ast/primitive_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.invokable_primitives import InvokablePrimitivesBase as _Base 2 | from som.vm.globals import nilObject 3 | from som.vmobjects.abstract_object import AbstractObject 4 | from som.vmobjects.array import Array 5 | from som.vmobjects.primitive import Primitive 6 | 7 | 8 | def _invoke_on_with(_ivkbl, rcvr, args): 9 | assert isinstance(rcvr, Primitive) 10 | assert isinstance(args[0], AbstractObject) 11 | assert isinstance(args[1], Array) or args[1] is nilObject 12 | 13 | if args[1] is nilObject: 14 | direct_args = [] 15 | else: 16 | direct_args = args[1].as_argument_array() 17 | return rcvr.invoke_args(args[0], direct_args) 18 | 19 | 20 | class PrimitivePrimitives(_Base): 21 | def install_primitives(self): 22 | _Base.install_primitives(self) 23 | self._install_instance_primitive(Primitive("invokeOn:with:", _invoke_on_with)) 24 | -------------------------------------------------------------------------------- /src/som/primitives/ast/string_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.string_primitives import StringPrimitivesBase as _Base 2 | 3 | 4 | StringPrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/ast/symbol_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.symbol_primitives import SymbolPrimitivesBase as _Base 2 | 3 | 4 | SymbolPrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/ast/system_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.system_primitives import SystemPrimitivesBase as _Base 2 | 3 | 4 | SystemPrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/ast/true_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.true_primitives import TruePrimitivesBase as _Base 2 | 3 | 4 | TruePrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/bc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOM-st/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/primitives/bc/__init__.py -------------------------------------------------------------------------------- /src/som/primitives/bc/array_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.array_primitives import ArrayPrimitivesBase as _Base 2 | 3 | 4 | ArrayPrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/bc/block_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.block_primitives import BlockPrimitivesBase as _Base 2 | 3 | 4 | BlockPrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/bc/class_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.class_primitives import ClassPrimitives as _Base 2 | 3 | 4 | ClassPrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/bc/double_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.double_primitives import DoublePrimitives as _Base 2 | 3 | 4 | DoublePrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/bc/false_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.false_primitives import FalsePrimitivesBase as _Base 2 | 3 | 4 | FalsePrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/bc/integer_primitives.py: -------------------------------------------------------------------------------- 1 | from rlib import jit 2 | 3 | from som.primitives.integer_primitives import IntegerPrimitivesBase as _Base 4 | from som.vmobjects.double import Double 5 | from som.vmobjects.integer import Integer 6 | from som.vmobjects.primitive import Primitive, TernaryPrimitive 7 | 8 | 9 | def get_printable_location_up(block_method): 10 | from som.vmobjects.method_bc import BcAbstractMethod 11 | 12 | assert isinstance(block_method, BcAbstractMethod) 13 | return "to:do: " + block_method.merge_point_string() 14 | 15 | 16 | jitdriver_int = jit.JitDriver( 17 | name="to:do: with int", 18 | greens=["block_method"], 19 | reds="auto", 20 | # virtualizables=['frame'], 21 | is_recursive=True, 22 | get_printable_location=get_printable_location_up, 23 | ) 24 | 25 | jitdriver_double = jit.JitDriver( 26 | name="to:do: with double", 27 | greens=["block_method"], 28 | reds="auto", 29 | # virtualizables=['frame'], 30 | is_recursive=True, 31 | get_printable_location=get_printable_location_up, 32 | ) 33 | 34 | 35 | def get_printable_location_down(block_method): 36 | from som.vmobjects.method_bc import BcAbstractMethod 37 | 38 | assert isinstance(block_method, BcAbstractMethod) 39 | return "downToto:do: " + block_method.merge_point_string() 40 | 41 | 42 | jitdriver_int_down = jit.JitDriver( 43 | name="downTo:do: with int", 44 | greens=["block_method"], 45 | reds="auto", 46 | # virtualizables=['frame'], 47 | is_recursive=True, 48 | get_printable_location=get_printable_location_down, 49 | ) 50 | 51 | jitdriver_double_down = jit.JitDriver( 52 | name="downTo:do: with double", 53 | greens=["block_method"], 54 | reds="auto", 55 | # virtualizables=['frame'], 56 | is_recursive=True, 57 | get_printable_location=get_printable_location_down, 58 | ) 59 | 60 | 61 | def _to_do_int(i, by_increment, top, block, block_method): 62 | assert isinstance(i, int) 63 | assert isinstance(top, int) 64 | while i <= top: 65 | jitdriver_int.jit_merge_point(block_method=block_method) 66 | 67 | block_method.invoke_2(block, Integer(i)) 68 | i += by_increment 69 | 70 | 71 | def _to_do_double(i, by_increment, top, block, block_method): 72 | assert isinstance(i, int) 73 | assert isinstance(top, float) 74 | while i <= top: 75 | jitdriver_double.jit_merge_point(block_method=block_method) 76 | 77 | block_method.invoke_2(block, Integer(i)) 78 | i += by_increment 79 | 80 | 81 | def _to_do(rcvr, limit, block): 82 | block_method = block.get_method() 83 | 84 | i = rcvr.get_embedded_integer() 85 | if isinstance(limit, Double): 86 | _to_do_double(i, 1, limit.get_embedded_double(), block, block_method) 87 | else: 88 | _to_do_int(i, 1, limit.get_embedded_integer(), block, block_method) 89 | 90 | return rcvr 91 | 92 | 93 | def _to_by_do(_ivkbl, stack, stack_ptr): 94 | block = stack[stack_ptr] 95 | stack[stack_ptr] = None 96 | stack_ptr -= 1 97 | 98 | by_increment = stack[stack_ptr] 99 | stack[stack_ptr] = None 100 | stack_ptr -= 1 101 | 102 | limit = stack[stack_ptr] 103 | stack[stack_ptr] = None 104 | stack_ptr -= 1 105 | 106 | block_method = block.get_method() 107 | 108 | self = stack[stack_ptr] 109 | 110 | i = self.get_embedded_integer() 111 | if isinstance(limit, Double): 112 | _to_do_double( 113 | i, 114 | by_increment.get_embedded_integer(), 115 | limit.get_embedded_double(), 116 | block, 117 | block_method, 118 | ) 119 | else: 120 | _to_do_int( 121 | i, 122 | by_increment.get_embedded_integer(), 123 | limit.get_embedded_integer(), 124 | block, 125 | block_method, 126 | ) 127 | 128 | return stack_ptr 129 | 130 | 131 | def _down_to_do_int(i, by_increment, bottom, block, block_method): 132 | assert isinstance(i, int) 133 | assert isinstance(bottom, int) 134 | while i >= bottom: 135 | jitdriver_int_down.jit_merge_point(block_method=block_method) 136 | 137 | block_method.invoke_2(block, Integer(i)) 138 | i -= by_increment 139 | 140 | 141 | def _down_to_do_double(i, by_increment, bottom, block, block_method): 142 | assert isinstance(i, int) 143 | assert isinstance(bottom, float) 144 | while i >= bottom: 145 | jitdriver_double_down.jit_merge_point(block_method=block_method) 146 | 147 | block_method.invoke_2(block, Integer(i)) 148 | i -= by_increment 149 | 150 | 151 | def _down_to_do(rcvr, limit, block): 152 | block_method = block.get_method() 153 | 154 | i = rcvr.get_embedded_integer() 155 | if isinstance(limit, Double): 156 | _down_to_do_double(i, 1, limit.get_embedded_double(), block, block_method) 157 | else: 158 | _down_to_do_int(i, 1, limit.get_embedded_integer(), block, block_method) 159 | 160 | return rcvr 161 | 162 | 163 | class IntegerPrimitives(_Base): 164 | def install_primitives(self): 165 | _Base.install_primitives(self) 166 | self._install_instance_primitive(TernaryPrimitive("to:do:", _to_do)) 167 | self._install_instance_primitive(TernaryPrimitive("downTo:do:", _down_to_do)) 168 | self._install_instance_primitive(Primitive("to:by:do:", _to_by_do)) 169 | -------------------------------------------------------------------------------- /src/som/primitives/bc/method_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.invokable_primitives import InvokablePrimitivesBase as _Base 2 | 3 | 4 | MethodPrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/bc/object_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.object_primitives import ObjectPrimitivesBase as _Base 2 | 3 | 4 | ObjectPrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/bc/primitive_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.invokable_primitives import InvokablePrimitivesBase as _Base 2 | 3 | 4 | PrimitivePrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/bc/string_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.string_primitives import StringPrimitivesBase as _Base 2 | 3 | 4 | StringPrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/bc/symbol_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.symbol_primitives import SymbolPrimitivesBase as _Base 2 | 3 | 4 | SymbolPrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/bc/system_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.system_primitives import SystemPrimitivesBase as _Base 2 | 3 | 4 | SystemPrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/bc/true_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.true_primitives import TruePrimitivesBase as _Base 2 | 3 | 4 | TruePrimitives = _Base 5 | -------------------------------------------------------------------------------- /src/som/primitives/block_primitives.py: -------------------------------------------------------------------------------- 1 | from rlib import jit 2 | 3 | from som.primitives.primitives import Primitives 4 | from som.vm.globals import nilObject, trueObject, falseObject 5 | from som.vmobjects.primitive import Primitive, BinaryPrimitive 6 | 7 | 8 | def _restart(ivkbl, rcvr, args): 9 | raise RuntimeError( 10 | "Restart primitive is not supported, #whileTrue: " 11 | "and #whileTrue: are intrisified so that #restart " 12 | "is not needed." 13 | ) 14 | 15 | 16 | def get_printable_location(method_body, method_condition, _while_type): 17 | from som.vmobjects.method import AbstractMethod 18 | 19 | assert isinstance(method_body, AbstractMethod) 20 | assert isinstance(method_condition, AbstractMethod) 21 | 22 | return "[%s] while [%s]" % ( 23 | method_condition.merge_point_string(), 24 | method_body.merge_point_string(), 25 | ) 26 | 27 | 28 | jitdriver = jit.JitDriver( 29 | greens=["method_body", "method_condition", "while_type"], 30 | reds="auto", 31 | # virtualizables=['frame'], 32 | is_recursive=True, 33 | get_printable_location=get_printable_location, 34 | ) 35 | 36 | 37 | def _while_loop(loop_condition, loop_body, while_type): 38 | method_body = loop_body.get_method() 39 | method_condition = loop_condition.get_method() 40 | 41 | while True: 42 | jitdriver.jit_merge_point( 43 | method_body=method_body, 44 | method_condition=method_condition, 45 | while_type=while_type, 46 | ) 47 | condition_result = method_condition.invoke_1(loop_condition) 48 | if condition_result is while_type: 49 | method_body.invoke_1(loop_body) 50 | else: 51 | break 52 | return nilObject 53 | 54 | 55 | def _while_false(rcvr, arg): 56 | return _while_loop(rcvr, arg, falseObject) 57 | 58 | 59 | def _while_true(rcvr, arg): 60 | return _while_loop(rcvr, arg, trueObject) 61 | 62 | 63 | class BlockPrimitivesBase(Primitives): 64 | def install_primitives(self): 65 | self._install_instance_primitive(BinaryPrimitive("whileTrue:", _while_true)) 66 | self._install_instance_primitive(BinaryPrimitive("whileFalse:", _while_false)) 67 | self._install_instance_primitive(Primitive("restart", _restart)) 68 | -------------------------------------------------------------------------------- /src/som/primitives/class_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.primitives import Primitives 2 | from som.vm.universe import Universe 3 | from som.vmobjects.primitive import UnaryPrimitive 4 | 5 | 6 | def _new(rcvr): 7 | return Universe.new_instance(rcvr) 8 | 9 | 10 | def _name(rcvr): 11 | return rcvr.get_name() 12 | 13 | 14 | def _super_class(rcvr): 15 | return rcvr.get_super_class() 16 | 17 | 18 | def _methods(rcvr): 19 | return rcvr.get_instance_invokables() 20 | 21 | 22 | def _fields(rcvr): 23 | return rcvr.get_instance_fields() 24 | 25 | 26 | class ClassPrimitives(Primitives): 27 | def install_primitives(self): 28 | self._install_instance_primitive(UnaryPrimitive("new", _new)) 29 | self._install_instance_primitive(UnaryPrimitive("name", _name)) 30 | self._install_instance_primitive(UnaryPrimitive("superclass", _super_class)) 31 | self._install_instance_primitive(UnaryPrimitive("methods", _methods)) 32 | self._install_instance_primitive(UnaryPrimitive("fields", _fields)) 33 | -------------------------------------------------------------------------------- /src/som/primitives/double_primitives.py: -------------------------------------------------------------------------------- 1 | from math import cos, sin, sqrt 2 | from rlib.float import round_double, INFINITY 3 | 4 | from som.primitives.primitives import Primitives 5 | from som.vm.globals import falseObject, trueObject 6 | from som.vmobjects.double import Double 7 | from som.vmobjects.primitive import UnaryPrimitive, BinaryPrimitive 8 | 9 | 10 | def _as_string(rcvr): 11 | return rcvr.prim_as_string() 12 | 13 | 14 | def _sqrt(rcvr): 15 | return Double(sqrt(rcvr.get_embedded_double())) 16 | 17 | 18 | def _plus(left, right): 19 | return left.prim_add(right) 20 | 21 | 22 | def _minus(left, right): 23 | return left.prim_subtract(right) 24 | 25 | 26 | def _mult(left, right): 27 | return left.prim_multiply(right) 28 | 29 | 30 | def _double_div(left, right): 31 | return left.prim_double_div(right) 32 | 33 | 34 | def _mod(left, right): 35 | return left.prim_modulo(right) 36 | 37 | 38 | def _equals(left, right): 39 | return left.prim_equals(right) 40 | 41 | 42 | def _unequals(left, right): 43 | return left.prim_unequals(right) 44 | 45 | 46 | def _min(left, right): 47 | if left.prim_less_than(right): 48 | return left 49 | return right 50 | 51 | 52 | def _max(left, right): 53 | if left.prim_less_than(right): 54 | return right 55 | return left 56 | 57 | 58 | def _less_than(left, right): 59 | if left.prim_less_than(right): 60 | return trueObject 61 | return falseObject 62 | 63 | 64 | def _less_than_or_equal(left, right): 65 | return left.prim_less_than_or_equal(right) 66 | 67 | 68 | def _greater_than(left, right): 69 | return left.prim_greater_than(right) 70 | 71 | 72 | def _greater_than_or_equal(left, right): 73 | return left.prim_greater_than_or_equal(right) 74 | 75 | 76 | def _round(rcvr): 77 | from som.vmobjects.integer import Integer 78 | 79 | int_value = int(round_double(rcvr.get_embedded_double(), 0)) 80 | return Integer(int_value) 81 | 82 | 83 | def _as_integer(rcvr): 84 | from som.vmobjects.integer import Integer 85 | 86 | return Integer(int(rcvr.get_embedded_double())) 87 | 88 | 89 | def _cos(rcvr): 90 | result = cos(rcvr.get_embedded_double()) 91 | return Double(result) 92 | 93 | 94 | def _sin(rcvr): 95 | result = sin(rcvr.get_embedded_double()) 96 | return Double(result) 97 | 98 | 99 | def _positive_infinity(_rcvr): 100 | return Double(INFINITY) 101 | 102 | 103 | def _from_string(_rcvr, string): 104 | try: 105 | return Double(float(string.get_embedded_string())) 106 | except ValueError: 107 | from som.vm.globals import nilObject 108 | 109 | return nilObject 110 | 111 | 112 | class DoublePrimitives(Primitives): 113 | def install_primitives(self): 114 | self._install_instance_primitive(UnaryPrimitive("asString", _as_string)) 115 | self._install_instance_primitive(UnaryPrimitive("round", _round)) 116 | self._install_instance_primitive(UnaryPrimitive("asInteger", _as_integer)) 117 | 118 | self._install_instance_primitive(UnaryPrimitive("sqrt", _sqrt)) 119 | self._install_instance_primitive(BinaryPrimitive("+", _plus)) 120 | self._install_instance_primitive(BinaryPrimitive("-", _minus)) 121 | self._install_instance_primitive(BinaryPrimitive("*", _mult)) 122 | self._install_instance_primitive(BinaryPrimitive("//", _double_div)) 123 | self._install_instance_primitive(BinaryPrimitive("%", _mod)) 124 | self._install_instance_primitive(BinaryPrimitive("=", _equals)) 125 | self._install_instance_primitive(BinaryPrimitive("<", _less_than)) 126 | self._install_instance_primitive(BinaryPrimitive("<=", _less_than_or_equal)) 127 | self._install_instance_primitive(BinaryPrimitive(">", _greater_than)) 128 | self._install_instance_primitive(BinaryPrimitive(">=", _greater_than_or_equal)) 129 | self._install_instance_primitive(BinaryPrimitive("<>", _unequals)) 130 | self._install_instance_primitive(BinaryPrimitive("~=", _unequals)) 131 | 132 | self._install_instance_primitive(BinaryPrimitive("max:", _max)) 133 | self._install_instance_primitive(BinaryPrimitive("min:", _min)) 134 | 135 | self._install_instance_primitive(UnaryPrimitive("sin", _sin)) 136 | self._install_instance_primitive(UnaryPrimitive("cos", _cos)) 137 | 138 | self._install_class_primitive( 139 | UnaryPrimitive("PositiveInfinity", _positive_infinity) 140 | ) 141 | self._install_class_primitive(BinaryPrimitive("fromString:", _from_string)) 142 | -------------------------------------------------------------------------------- /src/som/primitives/false_primitives.py: -------------------------------------------------------------------------------- 1 | from som.interp_type import is_ast_interpreter 2 | from som.primitives.primitives import Primitives 3 | from som.vm.globals import trueObject, falseObject, nilObject 4 | from som.vmobjects.primitive import UnaryPrimitive, BinaryPrimitive, TernaryPrimitive 5 | 6 | if is_ast_interpreter(): 7 | from som.vmobjects.block_ast import AstBlock as _Block 8 | else: 9 | from som.vmobjects.block_bc import BcBlock as _Block 10 | 11 | 12 | def _not(_rcvr): 13 | return trueObject 14 | 15 | 16 | def _and(_rcvr, _arg): 17 | return falseObject 18 | 19 | 20 | def _or_and_if_false(_rcvr, arg): 21 | if isinstance(arg, _Block): 22 | block_method = arg.get_method() 23 | return block_method.invoke_1(arg) 24 | return arg 25 | 26 | 27 | def _if_true(_rcvr, _arg): 28 | return nilObject 29 | 30 | 31 | def _if_true_if_false(_rcvr, _true_block, false_block): 32 | if isinstance(false_block, _Block): 33 | block_method = false_block.get_method() 34 | return block_method.invoke_1(false_block) 35 | return false_block 36 | 37 | 38 | class FalsePrimitivesBase(Primitives): 39 | def install_primitives(self): 40 | self._install_instance_primitive(UnaryPrimitive("not", _not)) 41 | self._install_instance_primitive(BinaryPrimitive("and:", _and)) 42 | self._install_instance_primitive(BinaryPrimitive("&&", _and)) 43 | 44 | self._install_instance_primitive(BinaryPrimitive("or:", _or_and_if_false)) 45 | self._install_instance_primitive(BinaryPrimitive("||", _or_and_if_false)) 46 | self._install_instance_primitive(BinaryPrimitive("ifFalse:", _or_and_if_false)) 47 | 48 | self._install_instance_primitive( 49 | TernaryPrimitive("ifTrue:ifFalse:", _if_true_if_false) 50 | ) 51 | -------------------------------------------------------------------------------- /src/som/primitives/invokable_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.primitives import Primitives 2 | from som.vmobjects.primitive import UnaryPrimitive 3 | 4 | 5 | def _holder(rcvr): 6 | return rcvr.get_holder() 7 | 8 | 9 | def _signature(rcvr): 10 | return rcvr.get_signature() 11 | 12 | 13 | class InvokablePrimitivesBase(Primitives): 14 | def install_primitives(self): 15 | self._install_instance_primitive(UnaryPrimitive("holder", _holder)) 16 | self._install_instance_primitive(UnaryPrimitive("signature", _signature)) 17 | -------------------------------------------------------------------------------- /src/som/primitives/known.py: -------------------------------------------------------------------------------- 1 | from functools import reduce 2 | from rlib.unroll import unrolling_iterable 3 | 4 | from som.interp_type import is_ast_interpreter, is_bytecode_interpreter 5 | 6 | """Captures the known primitives at load time of this module, i.e., at compile 7 | time with RPython. 8 | """ 9 | 10 | EXPECTED_NUMBER_OF_PRIMITIVE_FILES = 13 11 | 12 | 13 | class PrimitivesNotFound(Exception): 14 | pass 15 | 16 | 17 | def _is_primitives_class(entry_pair): 18 | "NOT_RPYTHON" 19 | from som.primitives.primitives import Primitives 20 | import inspect 21 | 22 | entry_name, entry = entry_pair 23 | 24 | return ( 25 | inspect.isclass(entry) 26 | and issubclass(entry, Primitives) 27 | and entry is not Primitives 28 | and entry is not None 29 | and entry_name is not None 30 | and not entry_name.startswith("_") 31 | ) 32 | 33 | 34 | def _setup_primitives(): 35 | "NOT_RPYTHON" 36 | from importlib import import_module 37 | import inspect 38 | from glob import glob 39 | 40 | base_package = "som.primitives." 41 | if is_ast_interpreter(): 42 | base_package += "ast." 43 | interp_dir = "ast" 44 | else: 45 | assert is_bytecode_interpreter() 46 | base_package += "bc." 47 | interp_dir = "bc" 48 | 49 | directory = ( 50 | __file__.replace("known.pyc", "").replace("known.py", "") + interp_dir + "/" 51 | ) 52 | files = glob(directory + "*_primitives.py") 53 | 54 | module_names = [f.replace(directory, "").replace(".py", "") for f in files] 55 | mods = [import_module(base_package + mod) for mod in module_names] 56 | all_members = [inspect.getmembers(mod) for mod in mods] 57 | all_members = reduce(lambda all, each: all + each, all_members) 58 | 59 | all_prims = filter(_is_primitives_class, all_members) 60 | 61 | prim_pairs = [ 62 | (prim_name[: prim_name.find("Primitives")], cls) 63 | for (prim_name, cls) in all_prims 64 | ] 65 | 66 | if EXPECTED_NUMBER_OF_PRIMITIVE_FILES != len(prim_pairs): 67 | print("") 68 | print("SOM PRIMITIVE DISCOVERY: following primitives found:") 69 | for name, _clazz in prim_pairs: 70 | print(" - %s" % name) 71 | print( 72 | "Expected number of primitive files: %d, found %d" 73 | % (EXPECTED_NUMBER_OF_PRIMITIVE_FILES, len(prim_pairs)) 74 | ) 75 | print("ERROR: did not find the expected number of primitive files!") 76 | import sys 77 | 78 | sys.exit(1) 79 | return prim_pairs 80 | 81 | 82 | _primitives = unrolling_iterable(_setup_primitives()) 83 | 84 | 85 | def primitives_for_class(cls): 86 | name = cls.get_name().get_embedded_string() 87 | for key, primitives in _primitives: 88 | if key == name: 89 | return primitives 90 | raise PrimitivesNotFound 91 | -------------------------------------------------------------------------------- /src/som/primitives/object_primitives.py: -------------------------------------------------------------------------------- 1 | from rlib.objectmodel import compute_identity_hash 2 | from som.primitives.primitives import Primitives 3 | from som.vm.current import current_universe 4 | 5 | from som.vm.globals import trueObject, falseObject 6 | from som.vmobjects.array import Array 7 | from som.vmobjects.object_with_layout import Object 8 | from som.vmobjects.primitive import UnaryPrimitive, BinaryPrimitive, TernaryPrimitive 9 | 10 | 11 | def _equals(op1, op2): 12 | if op1 is op2: 13 | return trueObject 14 | return falseObject 15 | 16 | 17 | def _object_size(rcvr): 18 | from som.vmobjects.integer import Integer 19 | 20 | size = 0 21 | 22 | if isinstance(rcvr, Object): 23 | size = rcvr.get_number_of_fields() 24 | elif isinstance(rcvr, Array): 25 | size = rcvr.get_number_of_indexable_fields() 26 | 27 | return Integer(size) 28 | 29 | 30 | def _hashcode(rcvr): 31 | from som.vmobjects.integer import Integer 32 | 33 | return Integer(compute_identity_hash(rcvr)) 34 | 35 | 36 | def _inst_var_at(rcvr, idx): 37 | return rcvr.get_field(idx.get_embedded_integer() - 1) 38 | 39 | 40 | def _inst_var_at_put(rcvr, idx, val): 41 | rcvr.set_field(idx.get_embedded_integer() - 1, val) 42 | return val 43 | 44 | 45 | def _inst_var_named(rcvr, arg): 46 | i = rcvr.get_field_index(arg) 47 | return rcvr.get_field(i) 48 | 49 | 50 | def _halt(rcvr): 51 | # noop 52 | print("BREAKPOINT") 53 | return rcvr 54 | 55 | 56 | def _class(rcvr): 57 | return rcvr.get_class(current_universe) 58 | 59 | 60 | def _perform(rcvr, selector): 61 | invokable = rcvr.get_object_layout(current_universe).lookup_invokable(selector) 62 | return invokable.invoke_1(rcvr) 63 | 64 | 65 | def _perform_in_superclass(rcvr, selector, clazz): 66 | invokable = clazz.lookup_invokable(selector) 67 | return invokable.invoke_1(rcvr) 68 | 69 | 70 | def _perform_with_arguments(rcvr, selector, args): 71 | num_args = args.get_number_of_indexable_fields() + 1 72 | 73 | invokable = rcvr.get_object_layout(current_universe).lookup_invokable(selector) 74 | 75 | if num_args == 1: 76 | return invokable.invoke_1(rcvr) 77 | if num_args == 2: 78 | return invokable.invoke_2(rcvr, args.get_indexable_field(0)) 79 | if num_args == 3: 80 | return invokable.invoke_3( 81 | rcvr, args.get_indexable_field(0), args.get_indexable_field(1) 82 | ) 83 | raise Exception("Not yet implemented") 84 | 85 | 86 | class ObjectPrimitivesBase(Primitives): 87 | def install_primitives(self): 88 | self._install_instance_primitive(BinaryPrimitive("==", _equals)) 89 | self._install_instance_primitive(UnaryPrimitive("hashcode", _hashcode)) 90 | self._install_instance_primitive(UnaryPrimitive("objectSize", _object_size)) 91 | self._install_instance_primitive(BinaryPrimitive("instVarAt:", _inst_var_at)) 92 | self._install_instance_primitive( 93 | TernaryPrimitive("instVarAt:put:", _inst_var_at_put) 94 | ) 95 | self._install_instance_primitive( 96 | BinaryPrimitive("instVarNamed:", _inst_var_named) 97 | ) 98 | 99 | self._install_instance_primitive(UnaryPrimitive("halt", _halt)) 100 | self._install_instance_primitive(UnaryPrimitive("class", _class)) 101 | 102 | self._install_instance_primitive(BinaryPrimitive("perform:", _perform)) 103 | self._install_instance_primitive( 104 | TernaryPrimitive("perform:inSuperclass:", _perform_in_superclass) 105 | ) 106 | self._install_instance_primitive( 107 | TernaryPrimitive("perform:withArguments:", _perform_with_arguments) 108 | ) 109 | -------------------------------------------------------------------------------- /src/som/primitives/primitives.py: -------------------------------------------------------------------------------- 1 | class Primitives(object): 2 | def __init__(self, universe): 3 | self.universe = universe 4 | self._holder = None 5 | 6 | def install_primitives_in(self, value): 7 | # Save a reference to the holder class 8 | self._holder = value 9 | 10 | # Install the primitives from this primitives class 11 | self.install_primitives() 12 | 13 | def install_primitives(self): 14 | raise NotImplementedError() 15 | 16 | def _install_instance_primitive(self, primitive, warn_if_not_existing=False): 17 | # Install the given primitive as an instance primitive in the holder class 18 | self._holder.add_primitive(primitive, warn_if_not_existing) 19 | 20 | def _install_class_primitive(self, primitive, warn_if_not_existing=False): 21 | # Install the given primitive as an instance primitive in the class of 22 | # the holder class 23 | self._holder.get_class(self.universe).add_primitive( 24 | primitive, warn_if_not_existing 25 | ) 26 | -------------------------------------------------------------------------------- /src/som/primitives/string_primitives.py: -------------------------------------------------------------------------------- 1 | from rlib.objectmodel import compute_hash 2 | from som.primitives.primitives import Primitives 3 | 4 | from som.vm.globals import trueObject, falseObject 5 | from som.vm.symbols import symbol_for 6 | from som.vmobjects.integer import Integer 7 | from som.vmobjects.primitive import UnaryPrimitive, BinaryPrimitive, TernaryPrimitive 8 | from som.vmobjects.string import String 9 | 10 | 11 | def _concat(rcvr, argument): 12 | return String(rcvr.get_embedded_string() + argument.get_embedded_string()) 13 | 14 | 15 | def _as_symbol(rcvr): 16 | return symbol_for(rcvr.get_embedded_string()) 17 | 18 | 19 | def _length(rcvr): 20 | return Integer(len(rcvr.get_embedded_string())) 21 | 22 | 23 | def _equals(op1, op2): 24 | if ( 25 | isinstance(op2, String) 26 | and op1.get_embedded_string() == op2.get_embedded_string() 27 | ): 28 | return trueObject 29 | return falseObject 30 | 31 | 32 | def _substring(rcvr, start, end): 33 | s = start.get_embedded_integer() - 1 34 | e = end.get_embedded_integer() 35 | string = rcvr.get_embedded_string() 36 | 37 | if s < 0 or s >= len(string) or e > len(string) or e < s: 38 | return String("Error - index out of bounds") 39 | return String(string[s:e]) 40 | 41 | 42 | def _char_at(rcvr, idx): 43 | i = idx.get_embedded_integer() - 1 44 | string = rcvr.get_embedded_string() 45 | 46 | if i < 0 or i >= len(string): 47 | return String("Error - index out of bounds") 48 | return String(string[i]) 49 | 50 | 51 | def _hashcode(rcvr): 52 | return Integer(compute_hash(rcvr.get_embedded_string())) 53 | 54 | 55 | def _is_whitespace(self): 56 | string = self.get_embedded_string() 57 | 58 | for char in string: 59 | if not char.isspace(): 60 | return falseObject 61 | 62 | if len(string) > 0: 63 | return trueObject 64 | return falseObject 65 | 66 | 67 | def _is_letters(self): 68 | string = self.get_embedded_string() 69 | 70 | for char in string: 71 | if not char.isalpha(): 72 | return falseObject 73 | 74 | if len(string) > 0: 75 | return trueObject 76 | return falseObject 77 | 78 | 79 | def _is_digits(self): 80 | string = self.get_embedded_string() 81 | 82 | for char in string: 83 | if not char.isdigit(): 84 | return falseObject 85 | 86 | if len(string) > 0: 87 | return trueObject 88 | return falseObject 89 | 90 | 91 | class StringPrimitivesBase(Primitives): 92 | def install_primitives(self): 93 | self._install_instance_primitive(BinaryPrimitive("charAt:", _char_at)) 94 | self._install_instance_primitive(BinaryPrimitive("concatenate:", _concat)) 95 | self._install_instance_primitive(UnaryPrimitive("asSymbol", _as_symbol)) 96 | self._install_instance_primitive(UnaryPrimitive("length", _length)) 97 | self._install_instance_primitive(BinaryPrimitive("=", _equals)) 98 | self._install_instance_primitive( 99 | TernaryPrimitive("primSubstringFrom:to:", _substring) 100 | ) 101 | self._install_instance_primitive(UnaryPrimitive("hashcode", _hashcode)) 102 | 103 | self._install_instance_primitive(UnaryPrimitive("isWhiteSpace", _is_whitespace)) 104 | self._install_instance_primitive(UnaryPrimitive("isLetters", _is_letters)) 105 | self._install_instance_primitive(UnaryPrimitive("isDigits", _is_digits)) 106 | -------------------------------------------------------------------------------- /src/som/primitives/symbol_primitives.py: -------------------------------------------------------------------------------- 1 | from som.primitives.primitives import Primitives 2 | from som.vmobjects.primitive import BinaryPrimitive, UnaryPrimitive 3 | from som.vm.globals import trueObject, falseObject 4 | from som.vmobjects.string import String 5 | 6 | 7 | def _as_string(rcvr): 8 | return String(rcvr.get_embedded_string()) 9 | 10 | 11 | def _equals(op1, op2): 12 | if op1 is op2: 13 | return trueObject 14 | 15 | if isinstance(op2, String): 16 | if op1.get_embedded_string() == op2.get_embedded_string(): 17 | return trueObject 18 | return falseObject 19 | 20 | 21 | class SymbolPrimitivesBase(Primitives): 22 | def install_primitives(self): 23 | self._install_instance_primitive(UnaryPrimitive("asString", _as_string)) 24 | self._install_instance_primitive(BinaryPrimitive("=", _equals), False) 25 | -------------------------------------------------------------------------------- /src/som/primitives/system_primitives.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from rlib import rgc, jit 4 | from rlib.streamio import open_file_as_stream, readall_from_stream 5 | 6 | from som.primitives.primitives import Primitives 7 | from som.vm.current import current_universe 8 | from som.vm.globals import nilObject, trueObject, falseObject 9 | from som.vm.universe import std_print, std_println, error_print, error_println 10 | from som.vmobjects.primitive import UnaryPrimitive, BinaryPrimitive, TernaryPrimitive 11 | 12 | 13 | def _load(_rcvr, arg): 14 | result = current_universe.load_class(arg) 15 | return result if result else nilObject 16 | 17 | 18 | def _exit(_rcvr, error): 19 | return current_universe.exit(error.get_embedded_integer()) 20 | 21 | 22 | def _global(_rcvr, argument): 23 | result = current_universe.get_global(argument) 24 | return result if result else nilObject 25 | 26 | 27 | def _has_global(_rcvr, arg): 28 | if current_universe.has_global(arg): 29 | return trueObject 30 | return falseObject 31 | 32 | 33 | def _global_put(_rcvr, argument, value): 34 | current_universe.set_global(argument, value) 35 | return value 36 | 37 | 38 | def _print_string(rcvr, argument): 39 | std_print(argument.get_embedded_string()) 40 | return rcvr 41 | 42 | 43 | def _print_newline(rcvr): 44 | std_println() 45 | return rcvr 46 | 47 | 48 | def _error_print(rcvr, string): 49 | error_print(string.get_embedded_string()) 50 | return rcvr 51 | 52 | 53 | def _error_println(rcvr, string): 54 | error_println(string.get_embedded_string()) 55 | return rcvr 56 | 57 | 58 | def _time(_rcvr): 59 | from som.vmobjects.integer import Integer 60 | 61 | since_start = time.time() - current_universe.start_time 62 | return Integer(int(since_start * 1000)) 63 | 64 | 65 | def _ticks(_rcvr): 66 | from som.vmobjects.integer import Integer 67 | 68 | since_start = time.time() - current_universe.start_time 69 | return Integer(int(since_start * 1000000)) 70 | 71 | 72 | @jit.dont_look_inside 73 | def _load_file(_rcvr, file_name): 74 | try: 75 | input_file = open_file_as_stream(file_name.get_embedded_string(), "r") 76 | try: 77 | result = readall_from_stream(input_file) 78 | from som.vmobjects.string import String 79 | 80 | return String(result) 81 | finally: 82 | input_file.close() 83 | except (OSError, IOError): 84 | pass 85 | return nilObject 86 | 87 | 88 | @jit.dont_look_inside 89 | def _full_gc(_rcvr): 90 | rgc.collect() 91 | return trueObject 92 | 93 | 94 | class SystemPrimitivesBase(Primitives): 95 | def install_primitives(self): 96 | self._install_instance_primitive(BinaryPrimitive("load:", _load)) 97 | self._install_instance_primitive(BinaryPrimitive("exit:", _exit)) 98 | self._install_instance_primitive(BinaryPrimitive("hasGlobal:", _has_global)) 99 | self._install_instance_primitive(BinaryPrimitive("global:", _global)) 100 | self._install_instance_primitive(TernaryPrimitive("global:put:", _global_put)) 101 | self._install_instance_primitive(BinaryPrimitive("printString:", _print_string)) 102 | self._install_instance_primitive(UnaryPrimitive("printNewline", _print_newline)) 103 | self._install_instance_primitive(BinaryPrimitive("errorPrint:", _error_print)) 104 | self._install_instance_primitive( 105 | BinaryPrimitive("errorPrintln:", _error_println) 106 | ) 107 | 108 | self._install_instance_primitive(UnaryPrimitive("time", _time)) 109 | self._install_instance_primitive(UnaryPrimitive("ticks", _ticks)) 110 | self._install_instance_primitive(UnaryPrimitive("fullGC", _full_gc)) 111 | 112 | self._install_instance_primitive(BinaryPrimitive("loadFile:", _load_file)) 113 | -------------------------------------------------------------------------------- /src/som/primitives/true_primitives.py: -------------------------------------------------------------------------------- 1 | from som.interp_type import is_ast_interpreter 2 | from som.primitives.primitives import Primitives 3 | from som.vm.globals import trueObject, falseObject, nilObject 4 | from som.vmobjects.primitive import UnaryPrimitive, BinaryPrimitive, TernaryPrimitive 5 | 6 | if is_ast_interpreter(): 7 | from som.vmobjects.block_ast import AstBlock as _Block 8 | else: 9 | from som.vmobjects.block_bc import BcBlock as _Block 10 | 11 | 12 | def _not(_rcvr): 13 | return falseObject 14 | 15 | 16 | def _or(_rcvr, _arg): 17 | return trueObject 18 | 19 | 20 | def _and_and_if_true(_rcvr, arg): 21 | if isinstance(arg, _Block): 22 | block_method = arg.get_method() 23 | return block_method.invoke_1(arg) 24 | return arg 25 | 26 | 27 | def _if_false(_rcvr, _arg): 28 | return nilObject 29 | 30 | 31 | def _if_true_if_false(_rcvr, true_block, _false_block): 32 | if isinstance(true_block, _Block): 33 | block_method = true_block.get_method() 34 | return block_method.invoke_1(true_block) 35 | return true_block 36 | 37 | 38 | class TruePrimitivesBase(Primitives): 39 | def install_primitives(self): 40 | self._install_instance_primitive(UnaryPrimitive("not", _not)) 41 | self._install_instance_primitive(BinaryPrimitive("or:", _or)) 42 | self._install_instance_primitive(BinaryPrimitive("||", _or)) 43 | 44 | self._install_instance_primitive(BinaryPrimitive("and:", _and_and_if_true)) 45 | self._install_instance_primitive(BinaryPrimitive("&&", _and_and_if_true)) 46 | self._install_instance_primitive(BinaryPrimitive("ifTrue:", _and_and_if_true)) 47 | self._install_instance_primitive(BinaryPrimitive("ifFalse:", _if_false)) 48 | 49 | self._install_instance_primitive( 50 | TernaryPrimitive("ifTrue:ifFalse:", _if_true_if_false) 51 | ) 52 | -------------------------------------------------------------------------------- /src/som/vm/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOM-st/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/vm/__init__.py -------------------------------------------------------------------------------- /src/som/vm/current.py: -------------------------------------------------------------------------------- 1 | def _init(): 2 | from som.vm.universe import create_universe 3 | 4 | return create_universe() 5 | 6 | 7 | current_universe = _init() 8 | -------------------------------------------------------------------------------- /src/som/vm/globals.py: -------------------------------------------------------------------------------- 1 | from som.vmobjects.object_without_fields import ObjectWithoutFields 2 | 3 | 4 | # The basic global objects 5 | nilObject = ObjectWithoutFields(None) 6 | trueObject = ObjectWithoutFields(None) 7 | falseObject = ObjectWithoutFields(None) 8 | -------------------------------------------------------------------------------- /src/som/vm/shell.py: -------------------------------------------------------------------------------- 1 | from rlib.exit import Exit 2 | from rlib.objectmodel import we_are_translated 3 | from rlib.osext import raw_input 4 | from som.compiler.parse_error import ParseError 5 | from som.vm.globals import nilObject 6 | from som.vm.symbols import symbol_for 7 | 8 | 9 | class Shell(object): 10 | def __init__(self, universe): 11 | self.universe = universe 12 | 13 | def start(self): 14 | from som.vm.universe import std_println, error_println 15 | 16 | counter = 0 17 | it = nilObject 18 | 19 | std_println('SOM Shell. Type "quit" to exit.\n') 20 | 21 | while True: 22 | try: 23 | # Read a statement from the keyboard 24 | stmt = raw_input(b"---> ") 25 | if stmt == "quit" or stmt == "": 26 | return it 27 | if stmt == "\n": 28 | continue 29 | 30 | # Generate a temporary class with a run method 31 | stmt = ( 32 | "Shell_Class_" 33 | + str(counter) 34 | + " = ( run: it = ( | tmp | tmp := (" 35 | + stmt 36 | + " ). 'it = ' print. ^tmp println ) )" 37 | ) 38 | counter += 1 39 | 40 | # Compile and load the newly generated class 41 | shell_class = self.universe.load_shell_class(stmt) 42 | 43 | # If success 44 | if shell_class: 45 | shell_object = self.universe.new_instance(shell_class) 46 | shell_method = shell_class.lookup_invokable(symbol_for("run:")) 47 | 48 | it = shell_method.invoke_2(shell_object, it) 49 | except ParseError as ex: 50 | error_println(str(ex)) 51 | except Exit as ex: 52 | raise ex 53 | except Exception as ex: # pylint: disable=broad-except 54 | if not we_are_translated(): # this cannot be done in rpython 55 | import traceback 56 | 57 | traceback.print_exc() 58 | error_println("Caught exception: %s" % ex) 59 | -------------------------------------------------------------------------------- /src/som/vm/symbols.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=invalid-name 2 | from rlib.jit import elidable 3 | from som.vmobjects.symbol import Symbol 4 | 5 | _symbol_table = {} 6 | 7 | 8 | @elidable 9 | def symbol_for(string): 10 | # Lookup the symbol in the symbol table 11 | result = _symbol_table.get(string, None) 12 | if result is not None: 13 | return result 14 | 15 | result = Symbol(string) 16 | # Insert the new symbol into the symbol table 17 | _symbol_table[string] = result 18 | return result 19 | 20 | 21 | sym_array = symbol_for("Array") 22 | sym_object = symbol_for("Object") 23 | sym_nil = symbol_for("nil") 24 | sym_true = symbol_for("true") 25 | sym_false = symbol_for("false") 26 | sym_plus = symbol_for("+") 27 | sym_minus = symbol_for("-") 28 | sym_array_size_placeholder = symbol_for("ArraySizeLiteralPlaceholder") 29 | 30 | sym_new_msg = symbol_for("new:") 31 | sym_at_put_msg = symbol_for("at:put:") 32 | -------------------------------------------------------------------------------- /src/som/vmobjects/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOM-st/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/vmobjects/__init__.py -------------------------------------------------------------------------------- /src/som/vmobjects/abstract_object.py: -------------------------------------------------------------------------------- 1 | class AbstractObject(object): 2 | def __init__(self): 3 | pass 4 | 5 | def get_class(self, universe): 6 | raise NotImplementedError("Subclasses need to implement get_class(universe).") 7 | 8 | def get_object_layout(self, universe): 9 | raise NotImplementedError( 10 | "Subclasses need to implement get_object_layout(universe)." 11 | ) 12 | 13 | @staticmethod 14 | def is_invokable(): 15 | return False 16 | 17 | def __str__(self): 18 | from som.vm.current import current_universe 19 | 20 | return "a " + self.get_class(current_universe).get_name().get_embedded_string() 21 | -------------------------------------------------------------------------------- /src/som/vmobjects/block_ast.py: -------------------------------------------------------------------------------- 1 | from rlib.jit import promote 2 | from som.interpreter.ast.frame import is_on_stack 3 | 4 | from som.vmobjects.abstract_object import AbstractObject 5 | 6 | 7 | VALUE_SIGNATURE = [ 8 | "UNUSED", # there aren't any 0-arg methods/blocks 9 | "value", 10 | "value:", 11 | "value:with:", 12 | ] 13 | 14 | 15 | class AstBlock(AbstractObject): 16 | _immutable_fields_ = ["_method", "_outer"] 17 | 18 | def __init__(self, method, context_values): 19 | AbstractObject.__init__(self) 20 | self._method = method 21 | self._outer = context_values 22 | 23 | def is_same_context(self, other_block): 24 | assert isinstance(other_block, AstBlock) 25 | return self._outer is other_block._outer # pylint: disable=protected-access 26 | 27 | def get_method(self): 28 | return promote(self._method) 29 | 30 | def get_from_outer(self, index): 31 | promote(index) 32 | assert 0 <= index < len(self._outer) 33 | return self._outer[index] 34 | 35 | def set_outer(self, index, value): 36 | promote(index) 37 | assert 0 <= index < len(self._outer) 38 | self._outer[index] = value 39 | 40 | def is_outer_on_stack(self): 41 | return is_on_stack(self._outer) 42 | 43 | def get_on_stack_marker(self): 44 | return self._outer 45 | 46 | def get_class(self, universe): 47 | return universe.block_classes[self._method.get_number_of_arguments()] 48 | 49 | def get_object_layout(self, universe): 50 | return universe.block_layouts[self._method.get_number_of_arguments()] 51 | -------------------------------------------------------------------------------- /src/som/vmobjects/block_bc.py: -------------------------------------------------------------------------------- 1 | from rlib.jit import promote 2 | from som.interpreter.ast.frame import is_on_stack 3 | 4 | from som.vmobjects.abstract_object import AbstractObject 5 | from som.vmobjects.block_ast import VALUE_SIGNATURE 6 | from som.vmobjects.primitive import ( 7 | UnaryPrimitive, 8 | BinaryPrimitive, 9 | TernaryPrimitive, 10 | ) 11 | 12 | 13 | class BcBlock(AbstractObject): 14 | _immutable_fields_ = ["_method", "_outer"] 15 | 16 | def __init__(self, method, inner): 17 | AbstractObject.__init__(self) 18 | self._method = method 19 | self._outer = inner 20 | 21 | def is_same_context(self, other_block): 22 | assert isinstance(other_block, BcBlock) 23 | return self._outer is other_block._outer # pylint: disable=protected-access 24 | 25 | def get_method(self): 26 | return promote(self._method) 27 | 28 | def get_from_outer(self, index): 29 | promote(index) 30 | assert self._outer and 0 <= index < len(self._outer), "No outer in " + str( 31 | self._method 32 | ) 33 | assert isinstance(self._outer[index], AbstractObject) 34 | return self._outer[index] 35 | 36 | def set_outer(self, index, value): 37 | promote(index) 38 | assert 0 <= index < len(self._outer) 39 | assert isinstance(value, AbstractObject) 40 | self._outer[index] = value 41 | 42 | def is_outer_on_stack(self): 43 | # TODO: can we get rid of this? 44 | # we may need a second bytecode for this (push block w/o context) 45 | if self._outer is None: 46 | return True 47 | return is_on_stack(self._outer) 48 | 49 | def get_on_stack_marker(self): 50 | return self._outer 51 | 52 | def get_class(self, universe): 53 | return universe.block_classes[self._method.get_number_of_arguments()] 54 | 55 | def get_object_layout(self, universe): 56 | return universe.block_layouts[self._method.get_number_of_arguments()] 57 | 58 | 59 | def block_evaluation_primitive(num_args): 60 | if num_args == 1: 61 | return UnaryPrimitive(VALUE_SIGNATURE[num_args], _invoke_1) 62 | if num_args == 2: 63 | return BinaryPrimitive(VALUE_SIGNATURE[num_args], _invoke_2) 64 | if num_args == 3: 65 | return TernaryPrimitive(VALUE_SIGNATURE[num_args], _invoke_3) 66 | raise Exception("Unsupported number of arguments for block: " + str(num_args)) 67 | 68 | 69 | def _invoke_1(rcvr): 70 | return rcvr.get_method().invoke_1(rcvr) 71 | 72 | 73 | def _invoke_2(rcvr, arg): 74 | return rcvr.get_method().invoke_2(rcvr, arg) 75 | 76 | 77 | def _invoke_3(rcvr, arg1, arg2): 78 | return rcvr.get_method().invoke_3(rcvr, arg1, arg2) 79 | -------------------------------------------------------------------------------- /src/som/vmobjects/double.py: -------------------------------------------------------------------------------- 1 | from math import fmod 2 | from rlib.float import float_to_str 3 | from som.vm.globals import trueObject, falseObject 4 | from som.vmobjects.abstract_object import AbstractObject 5 | 6 | 7 | class Double(AbstractObject): 8 | _immutable_fields_ = ["_embedded_double"] 9 | 10 | def __init__(self, value): 11 | AbstractObject.__init__(self) 12 | assert isinstance(value, float) 13 | self._embedded_double = value 14 | 15 | def get_embedded_double(self): 16 | return self._embedded_double 17 | 18 | def __str__(self): 19 | return str(self._embedded_double) 20 | 21 | def get_class(self, universe): 22 | return universe.double_class 23 | 24 | def get_object_layout(self, universe): 25 | return universe.double_layout 26 | 27 | @staticmethod 28 | def _get_float(obj): 29 | from som.vmobjects.integer import Integer 30 | from som.vmobjects.biginteger import BigInteger 31 | 32 | if isinstance(obj, Double): 33 | return obj.get_embedded_double() 34 | if isinstance(obj, Integer): 35 | return float(obj.get_embedded_integer()) 36 | if isinstance(obj, BigInteger): 37 | return obj.get_embedded_biginteger().tofloat() 38 | raise ValueError("Cannot coerce %s to Double!" % obj) 39 | 40 | def prim_multiply(self, right): 41 | r = self._get_float(right) 42 | return Double(self._embedded_double * r) 43 | 44 | def prim_inc(self): 45 | return Double(self._embedded_double + 1.0) 46 | 47 | def prim_dec(self): 48 | return Double(self._embedded_double - 1.0) 49 | 50 | def prim_add(self, right): 51 | r = self._get_float(right) 52 | return Double(self._embedded_double + r) 53 | 54 | def prim_bit_xor(self, right): 55 | raise NotImplementedError("bit operations on Double are not supported.") 56 | 57 | def prim_as_string(self): 58 | from som.vmobjects.string import String 59 | 60 | s = float_to_str(self._embedded_double) 61 | return String(s) 62 | 63 | def prim_subtract(self, right): 64 | r = self._get_float(right) 65 | return Double(self._embedded_double - r) 66 | 67 | def prim_double_div(self, right): 68 | r = self._get_float(right) 69 | return Double(self._embedded_double / r) 70 | 71 | def prim_int_div(self, right): 72 | from som.vmobjects.integer import Integer 73 | 74 | r = self._get_float(right) 75 | return Integer(int(self._embedded_double / r)) 76 | 77 | def prim_modulo(self, right): 78 | r = self._get_float(right) 79 | return Double(fmod(self._embedded_double, r)) 80 | 81 | def prim_remainder(self, right): 82 | r = self._get_float(right) 83 | return Double(fmod(self._embedded_double, r)) 84 | 85 | def prim_and(self, right): 86 | raise NotImplementedError("bit operations on Double are not supported.") 87 | 88 | def prim_equals(self, right): 89 | r = self._get_float(right) 90 | if self._embedded_double == r: 91 | return trueObject 92 | return falseObject 93 | 94 | def prim_unequals(self, right): 95 | r = self._get_float(right) 96 | if self._embedded_double != r: 97 | return trueObject 98 | return falseObject 99 | 100 | def prim_less_than(self, right): 101 | r = self._get_float(right) 102 | return self._embedded_double < r 103 | 104 | def prim_less_than_or_equal(self, right): 105 | r = self._get_float(right) 106 | if self._embedded_double <= r: 107 | return trueObject 108 | return falseObject 109 | 110 | def prim_greater_than(self, right): 111 | r = self._get_float(right) 112 | if self._embedded_double > r: 113 | return trueObject 114 | return falseObject 115 | 116 | def prim_greater_than_or_equal(self, right): 117 | r = self._get_float(right) 118 | if self._embedded_double >= r: 119 | return trueObject 120 | return falseObject 121 | -------------------------------------------------------------------------------- /src/som/vmobjects/method.py: -------------------------------------------------------------------------------- 1 | from rlib import jit 2 | from som.vmobjects.abstract_object import AbstractObject 3 | 4 | 5 | class AbstractMethod(AbstractObject): 6 | _immutable_fields_ = [ 7 | "_signature", 8 | "_holder", 9 | ] 10 | 11 | def __init__(self, signature): 12 | AbstractObject.__init__(self) 13 | self._signature = signature 14 | self._holder = None 15 | 16 | @staticmethod 17 | def is_primitive(): 18 | return False 19 | 20 | @staticmethod 21 | def is_invokable(): 22 | """We use this method to identify methods and primitives""" 23 | return True 24 | 25 | def get_holder(self): 26 | return self._holder 27 | 28 | # XXX this means that the JIT doesn't see changes to the method object 29 | @jit.elidable_promote("all") 30 | def get_signature(self): 31 | return self._signature 32 | 33 | def get_class(self, universe): 34 | return universe.method_class 35 | 36 | def get_object_layout(self, universe): 37 | return universe.method_layout 38 | 39 | def __str__(self): 40 | if self._holder: 41 | holder = self._holder.get_name().get_embedded_string() 42 | else: 43 | holder = "nil" 44 | return "Method(" + holder + ">>" + str(self._signature) + ")" 45 | 46 | def merge_point_string(self): 47 | """debug info for the jit""" 48 | if self._holder: 49 | holder = self._holder.get_name().get_embedded_string() 50 | else: 51 | holder = "nil" 52 | return holder + ">>" + self._signature.get_embedded_string() 53 | -------------------------------------------------------------------------------- /src/som/vmobjects/method_ast.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | from rlib import jit 4 | from rlib.debug import make_sure_not_resized 5 | from rtruffle.node import Node 6 | from som.interpreter.ast.frame import ( 7 | create_frame_args, 8 | create_frame_3, 9 | create_frame_2, 10 | create_frame_1, 11 | ) 12 | 13 | from som.vmobjects.method import AbstractMethod 14 | 15 | 16 | def get_printable_location(node): 17 | assert isinstance(node, AstMethod) 18 | return node.source_section.identifier 19 | 20 | 21 | jitdriver_1 = jit.JitDriver( 22 | greens=["node"], 23 | get_printable_location=get_printable_location, 24 | reds=["rcvr"], 25 | is_recursive=True, 26 | # the next line is a workaround around a likely bug in RPython 27 | # for some reason, the inlining heuristics default to "never inline" when 28 | # two different jit drivers are involved (in our case, the primitive 29 | # driver, and this one). 30 | # the next line says that calls involving this jitdriver should always be 31 | # inlined once (which means that things like Integer>>< will be inlined 32 | # into a while loop again, when enabling this driver). 33 | should_unroll_one_iteration=lambda self: True, 34 | ) 35 | 36 | jitdriver_2 = jit.JitDriver( 37 | greens=["node"], 38 | get_printable_location=get_printable_location, 39 | reds=["rcvr", "arg"], 40 | is_recursive=True, 41 | should_unroll_one_iteration=lambda self: True, 42 | ) 43 | 44 | jitdriver_3 = jit.JitDriver( 45 | greens=["node"], 46 | get_printable_location=get_printable_location, 47 | reds=["rcvr", "arg1", "arg2"], 48 | is_recursive=True, 49 | should_unroll_one_iteration=lambda self: True, 50 | ) 51 | 52 | jitdriver_args = jit.JitDriver( 53 | greens=["node"], 54 | get_printable_location=get_printable_location, 55 | reds=["rcvr", "args"], 56 | is_recursive=True, 57 | should_unroll_one_iteration=lambda self: True, 58 | ) 59 | 60 | 61 | class _Invokable(Node): 62 | """ 63 | Only needed to work around RPython type system. 64 | Otherwise the parent field would point to a non-Node type (AstMethod) 65 | """ 66 | 67 | _immutable_fields_ = ["expr_or_sequence?"] 68 | _child_nodes_ = ["expr_or_sequence"] 69 | 70 | def __init__(self, expr_or_sequence): 71 | Node.__init__(self) 72 | self.expr_or_sequence = self.adopt_child(expr_or_sequence) 73 | 74 | 75 | class AstMethod(AbstractMethod): 76 | _immutable_fields_ = [ 77 | "invokable", 78 | "_arg_inner_access[*]", 79 | "_size_frame", 80 | "_size_inner", 81 | "_embedded_block_methods", 82 | "source_section", 83 | "_lexical_scope", 84 | ] 85 | 86 | def __init__( 87 | self, 88 | signature, 89 | expr_or_sequence, 90 | arg_inner_access, 91 | size_frame, 92 | size_inner, 93 | embedded_block_methods, 94 | source_section, 95 | lexical_scope, 96 | ): 97 | AbstractMethod.__init__(self, signature) 98 | 99 | assert isinstance(arg_inner_access, list) 100 | make_sure_not_resized(arg_inner_access) 101 | 102 | self._arg_inner_access = arg_inner_access 103 | self._size_frame = size_frame 104 | self._size_inner = size_inner 105 | 106 | self._embedded_block_methods = embedded_block_methods 107 | self.source_section = source_section 108 | 109 | self.invokable = _Invokable(expr_or_sequence) 110 | 111 | self._lexical_scope = lexical_scope 112 | 113 | def set_holder(self, value): 114 | self._holder = value 115 | for method in self._embedded_block_methods: 116 | method.set_holder(value) 117 | 118 | @jit.elidable_promote("all") 119 | def get_number_of_arguments(self): 120 | return self._signature.get_number_of_signature_arguments() 121 | 122 | def invoke_1(node, rcvr): # pylint: disable=no-self-argument 123 | jitdriver_1.jit_merge_point(node=node, rcvr=rcvr) 124 | frame = create_frame_1( 125 | rcvr, 126 | node._size_frame, 127 | node._size_inner, 128 | ) 129 | return node.invokable.expr_or_sequence.execute(frame) 130 | 131 | def invoke_2(node, rcvr, arg): # pylint: disable=no-self-argument 132 | jitdriver_2.jit_merge_point(node=node, rcvr=rcvr, arg=arg) 133 | frame = create_frame_2( 134 | rcvr, 135 | arg, 136 | node._arg_inner_access[0], 137 | node._size_frame, 138 | node._size_inner, 139 | ) 140 | return node.invokable.expr_or_sequence.execute(frame) 141 | 142 | def invoke_3(node, rcvr, arg1, arg2): # pylint: disable=no-self-argument 143 | jitdriver_3.jit_merge_point(node=node, rcvr=rcvr, arg1=arg1, arg2=arg2) 144 | frame = create_frame_3( 145 | rcvr, 146 | arg1, 147 | arg2, 148 | node._arg_inner_access, 149 | node._size_frame, 150 | node._size_inner, 151 | ) 152 | return node.invokable.expr_or_sequence.execute(frame) 153 | 154 | def invoke_args(node, rcvr, args): # pylint: disable=no-self-argument 155 | assert args is not None 156 | make_sure_not_resized(args) 157 | 158 | jitdriver_args.jit_merge_point(node=node, rcvr=rcvr, args=args) 159 | frame = create_frame_args( 160 | rcvr, 161 | args, 162 | node._arg_inner_access, 163 | node._size_frame, 164 | node._size_inner, 165 | ) 166 | return node.invokable.expr_or_sequence.execute(frame) 167 | 168 | def inline(self, mgenc): 169 | mgenc.merge_into_scope(self._lexical_scope) 170 | self.invokable.expr_or_sequence.adapt_after_inlining(mgenc) 171 | return self.invokable.expr_or_sequence 172 | 173 | def adapt_after_outer_inlined(self, removed_ctx_level, mgenc_with_inlined): 174 | self.invokable.expr_or_sequence.adapt_after_outer_inlined( 175 | removed_ctx_level, mgenc_with_inlined 176 | ) 177 | if removed_ctx_level == 1: 178 | self._lexical_scope.drop_inlined_scope() 179 | -------------------------------------------------------------------------------- /src/som/vmobjects/object_without_fields.py: -------------------------------------------------------------------------------- 1 | from rlib.jit import promote 2 | 3 | from som.vmobjects.abstract_object import AbstractObject 4 | 5 | 6 | class ObjectWithoutFields(AbstractObject): 7 | _immutable_fields_ = ["_object_layout?"] 8 | 9 | def __init__(self, layout): # pylint: disable=W 10 | self._object_layout = layout 11 | 12 | def get_class(self, universe): 13 | assert self._object_layout is not None 14 | return self._object_layout.for_class 15 | 16 | def get_object_layout(self, _universe): 17 | return promote(self._object_layout) 18 | 19 | def set_class(self, clazz): 20 | layout = clazz.get_layout_for_instances() 21 | assert layout is not None 22 | self._object_layout = layout 23 | 24 | def get_number_of_fields(self): 25 | return 0 26 | 27 | def __str__(self): 28 | from som.vm.globals import nilObject, trueObject, falseObject 29 | 30 | if self is nilObject: 31 | return "nil" 32 | if self is trueObject: 33 | return "true" 34 | if self is falseObject: 35 | return "false" 36 | return AbstractObject.__str__(self) 37 | -------------------------------------------------------------------------------- /src/som/vmobjects/primitive.py: -------------------------------------------------------------------------------- 1 | from som.interp_type import is_ast_interpreter 2 | from som.vm.symbols import symbol_for 3 | from som.vmobjects.abstract_object import AbstractObject 4 | 5 | 6 | class _AbstractPrimitive(AbstractObject): 7 | _immutable_fields_ = ["_is_empty", "_signature", "_holder"] 8 | 9 | def __init__(self, signature_string, is_empty=False): 10 | AbstractObject.__init__(self) 11 | 12 | self._signature = symbol_for(signature_string) 13 | self._is_empty = is_empty 14 | self._holder = None 15 | 16 | @staticmethod 17 | def is_primitive(): 18 | return True 19 | 20 | @staticmethod 21 | def is_invokable(): 22 | """We use this method to identify methods and primitives""" 23 | return True 24 | 25 | def get_signature(self): 26 | return self._signature 27 | 28 | def get_holder(self): 29 | return self._holder 30 | 31 | def set_holder(self, value): 32 | self._holder = value 33 | 34 | def is_empty(self): 35 | # By default a primitive is not empty 36 | return self._is_empty 37 | 38 | def get_class(self, universe): 39 | return universe.primitive_class 40 | 41 | def get_object_layout(self, universe): 42 | return universe.primitive_layout 43 | 44 | def __str__(self): 45 | if self._holder: 46 | holder = self.get_holder().get_name().get_embedded_string() 47 | else: 48 | holder = "nil" 49 | return "Primitive(" + holder + ">>" + str(self.get_signature()) + ")" 50 | 51 | 52 | class _AstPrimitive(_AbstractPrimitive): 53 | _immutable_fields_ = ["_prim_fn"] 54 | 55 | def __init__(self, signature_string, prim_fn, is_empty=False): 56 | _AbstractPrimitive.__init__(self, signature_string, is_empty) 57 | self._prim_fn = prim_fn 58 | 59 | def invoke_args(self, rcvr, args): 60 | prim_fn = self._prim_fn 61 | return prim_fn(self, rcvr, args) 62 | 63 | 64 | class _BcPrimitive(_AbstractPrimitive): 65 | _immutable_fields_ = ["_prim_fn"] 66 | 67 | def __init__(self, signature_string, prim_fn, is_empty=False): 68 | _AbstractPrimitive.__init__(self, signature_string, is_empty) 69 | self._prim_fn = prim_fn 70 | 71 | def invoke_n(self, stack, stack_ptr): 72 | prim_fn = self._prim_fn 73 | return prim_fn(self, stack, stack_ptr) 74 | 75 | def get_number_of_signature_arguments(self): 76 | return self._signature.get_number_of_signature_arguments() 77 | 78 | 79 | class UnaryPrimitive(_AbstractPrimitive): 80 | _immutable_fields_ = ["_prim_fn"] 81 | 82 | def __init__(self, signature_string, prim_fn, is_empty=False): 83 | _AbstractPrimitive.__init__(self, signature_string, is_empty) 84 | self._prim_fn = prim_fn 85 | 86 | def invoke_1(self, rcvr): 87 | prim_fn = self._prim_fn 88 | return prim_fn(rcvr) 89 | 90 | def get_number_of_signature_arguments(self): 91 | return 1 92 | 93 | 94 | class BinaryPrimitive(_AbstractPrimitive): 95 | _immutable_fields_ = ["_prim_fn"] 96 | 97 | def __init__(self, signature_string, prim_fn, is_empty=False): 98 | _AbstractPrimitive.__init__(self, signature_string, is_empty) 99 | self._prim_fn = prim_fn 100 | 101 | def invoke_2(self, rcvr, arg): 102 | prim_fn = self._prim_fn 103 | return prim_fn(rcvr, arg) 104 | 105 | def get_number_of_signature_arguments(self): 106 | return 2 107 | 108 | 109 | class TernaryPrimitive(_AbstractPrimitive): 110 | _immutable_fields_ = ["_prim_fn"] 111 | 112 | def __init__(self, signature_string, prim_fn, is_empty=False): 113 | _AbstractPrimitive.__init__(self, signature_string, is_empty) 114 | self._prim_fn = prim_fn 115 | 116 | def invoke_3(self, rcvr, arg1, arg2): 117 | prim_fn = self._prim_fn 118 | return prim_fn(rcvr, arg1, arg2) 119 | 120 | def get_number_of_signature_arguments(self): 121 | return 3 122 | 123 | 124 | def _empty_invoke_ast(ivkbl, _rcvr, _args): 125 | """Write a warning to the screen""" 126 | print( 127 | "Warning: undefined primitive #%s called" 128 | % ivkbl.get_signature().get_embedded_string() 129 | ) 130 | 131 | 132 | def _empty_invoke_bc(ivkbl, _stack, stack_ptr): 133 | """Write a warning to the screen""" 134 | print( 135 | "Warning: undefined primitive #%s called" 136 | % ivkbl.get_signature().get_embedded_string() 137 | ) 138 | return stack_ptr 139 | 140 | 141 | if is_ast_interpreter(): 142 | Primitive = _AstPrimitive 143 | _empty_invoke = _empty_invoke_ast 144 | else: 145 | Primitive = _BcPrimitive 146 | _empty_invoke = _empty_invoke_bc 147 | 148 | 149 | def empty_primitive(signature_string): 150 | """Return an empty primitive with the given signature""" 151 | return Primitive(signature_string, _empty_invoke, True) 152 | -------------------------------------------------------------------------------- /src/som/vmobjects/string.py: -------------------------------------------------------------------------------- 1 | from som.vmobjects.abstract_object import AbstractObject 2 | 3 | 4 | class String(AbstractObject): 5 | _immutable_fields_ = ["_string"] 6 | 7 | def __init__(self, value): 8 | AbstractObject.__init__(self) 9 | self._string = value 10 | 11 | def get_embedded_string(self): 12 | return self._string 13 | 14 | def __str__(self): 15 | return '"' + self._string + '"' 16 | 17 | def get_class(self, universe): 18 | return universe.string_class 19 | 20 | def get_object_layout(self, universe): 21 | return universe.string_layout 22 | -------------------------------------------------------------------------------- /src/som/vmobjects/symbol.py: -------------------------------------------------------------------------------- 1 | from som.vmobjects.string import String 2 | 3 | 4 | class Symbol(String): 5 | _immutable_fields_ = ["_string", "_number_of_signature_arguments"] 6 | 7 | def __init__(self, value): 8 | String.__init__(self, value) 9 | self._number_of_signature_arguments = ( 10 | self._determine_number_of_signature_arguments() 11 | ) # updated later 12 | 13 | def _determine_number_of_signature_arguments(self): 14 | # Check for binary signature 15 | if self._is_binary_signature(): 16 | return 2 17 | # Count the colons in the signature string 18 | number_of_colons = 0 19 | 20 | # Iterate through every character in the signature string 21 | for c in self._string: 22 | if c == ":": 23 | number_of_colons += 1 24 | 25 | # The number of arguments is equal to the number of colons plus one 26 | return number_of_colons + 1 27 | 28 | def get_number_of_signature_arguments(self): 29 | return self._number_of_signature_arguments 30 | 31 | def _is_binary_signature(self): 32 | # Check the individual characters of the string 33 | for c in self._string: 34 | if ( 35 | c != "~" 36 | and c != "&" 37 | and c != "|" 38 | and c != "*" 39 | and c != "/" 40 | and c != "@" 41 | and c != "+" 42 | and c != "-" 43 | and c != "=" 44 | and c != ">" 45 | and c != "<" 46 | and c != "," 47 | and c != "%" 48 | and c != "\\" 49 | ): 50 | return False 51 | return True 52 | 53 | def __str__(self): 54 | return "#" + self._string 55 | 56 | def get_class(self, universe): 57 | return universe.symbol_class 58 | 59 | def get_object_layout(self, universe): 60 | return universe.symbol_layout 61 | -------------------------------------------------------------------------------- /tests/rtruffle_tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SOM-st/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/tests/rtruffle_tests/__init__.py -------------------------------------------------------------------------------- /tests/rtruffle_tests/test_node.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from rtruffle.node import Node 3 | 4 | 5 | class NodeTest(unittest.TestCase): 6 | def test_adopt_child(self): 7 | child = ChildNode() 8 | parent = RootNode() 9 | 10 | self.assertIsNone(child.parent) 11 | parent.adopt_child(child) 12 | 13 | self.assertIs(parent, child.parent) 14 | 15 | def test_adopt_children(self): 16 | children = [ChildNode() for _ in range(0, 10)] 17 | parent = RootNode() 18 | 19 | self.assertIsNot(children[0], children[1]) 20 | for child in children: 21 | self.assertIsNone(child.parent) 22 | 23 | parent.adopt_children(children) 24 | for child in children: 25 | self.assertIs(parent, child.parent) 26 | 27 | def test_replace_1(self): 28 | child1 = ChildNode() 29 | parent = RootNode(child1, None) 30 | 31 | self.assertIs(child1, parent.child_node1) 32 | self.assertIsNone(parent.child_node2) 33 | 34 | child2 = ChildNode() 35 | child1.replace(child2) 36 | 37 | self.assertIs(child2, parent.child_node1) 38 | self.assertIsNone(parent.child_node2) 39 | 40 | def test_replace_2(self): 41 | child1 = ChildNode() 42 | parent = RootNode(None, child1) 43 | 44 | self.assertIsNone(parent.child_node1) 45 | self.assertIs(child1, parent.child_node2) 46 | 47 | child2 = ChildNode() 48 | child1.replace(child2) 49 | 50 | self.assertIsNone(parent.child_node1) 51 | self.assertIs(child2, parent.child_node2) 52 | 53 | def test_replace_in_children(self): 54 | child1 = ChildNode() 55 | child2 = ChildNode() 56 | parent = RootNodeWithChildList([child1, child1, child1]) 57 | 58 | for each in parent.child_nodes: 59 | self.assertIs(each, child1) 60 | 61 | child1.replace(child2) 62 | 63 | for each in parent.child_nodes: 64 | self.assertIs(each, child2) 65 | 66 | 67 | class RootNode(Node): 68 | _child_nodes_ = ["child_node1", "child_node2"] 69 | 70 | def __init__(self, child_node1=None, child_node2=None): 71 | Node.__init__(self) 72 | self.child_node1 = self.adopt_child(child_node1) 73 | self.child_node2 = self.adopt_child(child_node2) 74 | 75 | 76 | class RootNodeWithChildList(Node): 77 | _child_nodes_ = ["child_nodes[*]"] 78 | 79 | def __init__(self, child_nodes=None): 80 | Node.__init__(self) 81 | assert isinstance(child_nodes, list) 82 | self.child_nodes = self.adopt_children(child_nodes) 83 | 84 | 85 | class ChildNode(Node): 86 | pass 87 | -------------------------------------------------------------------------------- /tests/test_array.py: -------------------------------------------------------------------------------- 1 | from som.vm.globals import trueObject 2 | from som.vmobjects.array import Array 3 | from som.vmobjects.array import _empty_strategy # pylint: disable=protected-access 4 | from som.vmobjects.array import _obj_strategy # pylint: disable=protected-access 5 | from som.vmobjects.array import _long_strategy # pylint: disable=protected-access 6 | from som.vmobjects.array import _partially_empty_strategy # pylint: disable=W 7 | from som.vmobjects.array import _bool_strategy # pylint: disable=protected-access 8 | 9 | from som.vmobjects.integer import Integer 10 | 11 | 12 | def test_empty_array(): 13 | arr = Array.from_size(0) 14 | assert arr.strategy is _empty_strategy 15 | 16 | 17 | def test_empty_to_obj(): 18 | arr = Array.from_size(1) 19 | assert arr.strategy is _empty_strategy 20 | 21 | arr.set_indexable_field(0, arr) 22 | assert arr.strategy is _obj_strategy 23 | assert arr is arr.get_indexable_field(0) 24 | 25 | 26 | def test_empty_to_int(): 27 | arr = Array.from_size(1) 28 | assert arr.strategy is _empty_strategy 29 | 30 | int_obj = Integer(42) 31 | 32 | arr.set_indexable_field(0, int_obj) 33 | assert arr.strategy is _long_strategy 34 | assert arr.get_indexable_field(0).get_embedded_integer() == 42 35 | 36 | 37 | def test_empty_to_bool(): 38 | arr = Array.from_size(1) 39 | assert arr.strategy is _empty_strategy 40 | 41 | arr.set_indexable_field(0, trueObject) 42 | assert arr.strategy is _bool_strategy 43 | assert trueObject is arr.get_indexable_field(0) 44 | 45 | 46 | def test_copy_and_extend_partially_empty(): 47 | arr = Array.from_size(3) 48 | 49 | int_obj = Integer(42) 50 | arr.set_indexable_field(0, int_obj) 51 | assert arr.strategy is _partially_empty_strategy 52 | new_arr = arr.copy_and_extend_with(int_obj) 53 | 54 | assert arr is not new_arr 55 | assert new_arr.get_number_of_indexable_fields() == 4 56 | assert new_arr.strategy is _partially_empty_strategy 57 | -------------------------------------------------------------------------------- /tests/test_bc_frame.py: -------------------------------------------------------------------------------- 1 | from som.interpreter.ast.frame import ( 2 | FRAME_AND_INNER_RCVR_IDX, 3 | read_frame, 4 | read_inner, 5 | create_frame_1, 6 | create_frame_2, 7 | ) 8 | from som.interpreter.bc.frame import ( 9 | create_frame, 10 | ) 11 | from som.vmobjects.integer import Integer 12 | 13 | _MIN_FRAME_SIZE = 1 + 1 # Inner, Receiver 14 | 15 | 16 | def test_call_argument_handling_all_frame(): 17 | num_args = 4 18 | prev_stack = [Integer(i) for i in range(num_args)] 19 | 20 | callee_frame = create_frame( 21 | [False] * (num_args - 1), 22 | _MIN_FRAME_SIZE + num_args, 23 | 0, 24 | prev_stack, 25 | num_args - 1, 26 | num_args, 27 | ) 28 | 29 | for i in range(num_args): 30 | assert ( 31 | i 32 | == read_frame( 33 | callee_frame, FRAME_AND_INNER_RCVR_IDX + i 34 | ).get_embedded_integer() 35 | ) 36 | 37 | 38 | def test_call_argument_handling_mix_frame_and_inner(): 39 | num_args = 6 40 | prev_stack = [Integer(i) for i in range(num_args)] 41 | 42 | arg_access_inner = [True, False, True, False, True] 43 | arg_access_inner.reverse() 44 | callee_frame = create_frame( 45 | arg_access_inner, 46 | _MIN_FRAME_SIZE + num_args, 47 | 2 + 3, 48 | prev_stack, 49 | num_args - 1, 50 | num_args, 51 | ) 52 | 53 | assert ( 54 | read_frame(callee_frame, FRAME_AND_INNER_RCVR_IDX).get_embedded_integer() == 0 55 | ) 56 | assert ( 57 | read_inner(callee_frame, FRAME_AND_INNER_RCVR_IDX + 1).get_embedded_integer() 58 | == 1 59 | ) 60 | assert ( 61 | read_frame(callee_frame, FRAME_AND_INNER_RCVR_IDX + 1).get_embedded_integer() 62 | == 2 63 | ) 64 | assert ( 65 | read_inner(callee_frame, FRAME_AND_INNER_RCVR_IDX + 2).get_embedded_integer() 66 | == 3 67 | ) 68 | assert ( 69 | read_frame(callee_frame, FRAME_AND_INNER_RCVR_IDX + 2).get_embedded_integer() 70 | == 4 71 | ) 72 | 73 | assert ( 74 | read_inner(callee_frame, FRAME_AND_INNER_RCVR_IDX + 3).get_embedded_integer() 75 | == 5 76 | ) 77 | 78 | 79 | def test_call_argument_handling_first_frame_then_inner(): 80 | num_args = 6 81 | 82 | prev_stack = [Integer(i) for i in range(num_args)] 83 | 84 | arg_access_inner = [False, False, False, True, True] 85 | arg_access_inner.reverse() 86 | callee_frame = create_frame( 87 | arg_access_inner, 88 | _MIN_FRAME_SIZE + num_args, 89 | 2 + 3, 90 | prev_stack, 91 | num_args - 1, 92 | num_args, 93 | ) 94 | 95 | assert ( 96 | read_frame(callee_frame, FRAME_AND_INNER_RCVR_IDX).get_embedded_integer() == 0 97 | ) 98 | assert ( 99 | read_frame(callee_frame, FRAME_AND_INNER_RCVR_IDX + 1).get_embedded_integer() 100 | == 1 101 | ) 102 | assert ( 103 | read_frame(callee_frame, FRAME_AND_INNER_RCVR_IDX + 2).get_embedded_integer() 104 | == 2 105 | ) 106 | assert ( 107 | read_frame(callee_frame, FRAME_AND_INNER_RCVR_IDX + 3).get_embedded_integer() 108 | == 3 109 | ) 110 | assert ( 111 | read_inner(callee_frame, FRAME_AND_INNER_RCVR_IDX + 1).get_embedded_integer() 112 | == 4 113 | ) 114 | assert ( 115 | read_inner(callee_frame, FRAME_AND_INNER_RCVR_IDX + 2).get_embedded_integer() 116 | == 5 117 | ) 118 | 119 | 120 | def test_create_frame_1(): 121 | rcvr = Integer(1) 122 | frame = create_frame_1(rcvr, _MIN_FRAME_SIZE, _MIN_FRAME_SIZE) 123 | 124 | assert read_inner(frame, FRAME_AND_INNER_RCVR_IDX).get_embedded_integer() == 1 125 | assert read_frame(frame, FRAME_AND_INNER_RCVR_IDX).get_embedded_integer() == 1 126 | 127 | 128 | def test_create_frame_2_inner(): 129 | rcvr = Integer(1) 130 | arg = Integer(2) 131 | frame = create_frame_2(rcvr, arg, True, _MIN_FRAME_SIZE, _MIN_FRAME_SIZE + 1) 132 | 133 | assert read_inner(frame, FRAME_AND_INNER_RCVR_IDX).get_embedded_integer() == 1 134 | assert read_frame(frame, FRAME_AND_INNER_RCVR_IDX).get_embedded_integer() == 1 135 | assert read_inner(frame, FRAME_AND_INNER_RCVR_IDX + 1).get_embedded_integer() == 2 136 | 137 | 138 | def test_create_frame_2_frame(): 139 | rcvr = Integer(1) 140 | arg = Integer(2) 141 | frame = create_frame_2(rcvr, arg, False, _MIN_FRAME_SIZE + 1, _MIN_FRAME_SIZE) 142 | 143 | assert read_inner(frame, FRAME_AND_INNER_RCVR_IDX).get_embedded_integer() == 1 144 | assert read_frame(frame, FRAME_AND_INNER_RCVR_IDX).get_embedded_integer() == 1 145 | assert read_frame(frame, FRAME_AND_INNER_RCVR_IDX + 1).get_embedded_integer() == 2 146 | -------------------------------------------------------------------------------- /tests/test_printable_locations.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=redefined-outer-name 2 | import pytest 3 | 4 | from rtruffle.source_section import SourceCoordinate, SourceSection 5 | from som.interpreter.ast.nodes.specialized.down_to_do_node import ( 6 | get_printable_location as pl_dtd, 7 | ) 8 | from som.interpreter.ast.nodes.specialized.literal_while import ( 9 | get_printable_location_while as pl_while, 10 | WhileInlinedNode, 11 | ) 12 | from som.interpreter.ast.nodes.specialized.to_by_do_node import ( 13 | get_printable_location as pl_tbd, 14 | ) 15 | from som.interpreter.ast.nodes.specialized.to_do_node import ( 16 | get_printable_location as pl_td, 17 | ) 18 | from som.vm.symbols import symbol_for 19 | from som.vmobjects.clazz import Class 20 | from som.vmobjects.method_ast import AstMethod, get_printable_location as pl_method 21 | 22 | 23 | @pytest.fixture 24 | def method(source_section): 25 | sig = symbol_for("test") 26 | clazz = Class() 27 | clazz.set_name(symbol_for("Test")) 28 | method = AstMethod(sig, None, [], 0, 0, [], None, None) 29 | method.set_holder(clazz) 30 | method.source_section = source_section 31 | return method 32 | 33 | 34 | @pytest.fixture 35 | def source_section(): 36 | coord = SourceCoordinate(1, 1, 1) 37 | return SourceSection(None, "Test>>test", coord, 0, "test.som") 38 | 39 | 40 | def test_pl_dtd(method): 41 | assert pl_dtd(method) == "#to:do: Test>>test" 42 | 43 | 44 | def test_while(source_section): 45 | node = WhileInlinedNode(None, None, None, source_section) 46 | assert pl_while(node) == "while test.som:1:1" 47 | 48 | 49 | def test_tbd(method): 50 | assert pl_tbd(method) == "#to:do: Test>>test" 51 | 52 | 53 | def test_td(method): 54 | assert pl_td(method) == "#to:do: Test>>test" 55 | 56 | 57 | def test_method(method): 58 | assert pl_method(method) == "Test>>test" 59 | -------------------------------------------------------------------------------- /tests/test_som.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | 4 | from som.vm.current import current_universe 5 | 6 | 7 | @pytest.mark.parametrize( 8 | "test_name", 9 | [ 10 | "Array", 11 | "Block", 12 | "ClassLoading", 13 | "ClassStructure", 14 | "Closure", 15 | "Coercion", 16 | "CompilerReturn", 17 | "DoesNotUnderstand", 18 | "Double", 19 | "Empty", 20 | "Global", 21 | "Hash", 22 | "Integer", 23 | "Preliminary", 24 | "Reflection", 25 | "SelfBlock", 26 | "SpecialSelectors", 27 | "Super", 28 | "Set", 29 | "String", 30 | "Symbol", 31 | "System", 32 | "Vector", 33 | ], 34 | ) 35 | def test_som(test_name): 36 | current_universe.reset(True) 37 | core_lib_path = os.path.dirname(os.path.abspath(__file__)) + "/../core-lib/" 38 | args = [ 39 | "-cp", 40 | core_lib_path + "Smalltalk", 41 | core_lib_path + "TestSuite/TestHarness.som", 42 | test_name, 43 | ] 44 | 45 | current_universe.interpret(args) 46 | 47 | assert current_universe.last_exit_code() == 0 48 | -------------------------------------------------------------------------------- /trace-filter.py: -------------------------------------------------------------------------------- 1 | # This script will remove things from the input that make it harder to diff 2 | # traces. 3 | # 4 | # To generate a trace, for instance run: 5 | # PYPYLOG=jit-log-opt:List-new.log ./som-ast-jit -cp Smalltalk:Examples/Benchmarks/LanguageFeatures Examples/Benchmarks/BenchmarkHarness.som List 65 0 100 6 | # 7 | # Afterwards, the file can be filtered, for instance with: 8 | # cat List-new.log | python trace-filter.py > List-new.log-filtered 9 | # 10 | import fileinput 11 | import sys 12 | 13 | import re 14 | 15 | pointer = re.compile("0x[0-9a-f]{9,12}") 16 | target_token = re.compile("TargetToken\\([0-9]*\\)") 17 | address = re.compile("\\[[0-9a-f]{8,12}]") 18 | line_num = re.compile("^\\+\\d*:") 19 | long_number = re.compile("\\d{8,}") 20 | 21 | for line in fileinput.input(): 22 | filtered_line = re.sub(pointer, '(ptr)', line) 23 | filtered_line = re.sub(target_token, 'TargetToken(tkn)', filtered_line) 24 | filtered_line = re.sub(address, '[adr]', filtered_line) 25 | filtered_line = re.sub(line_num, '', filtered_line) 26 | filtered_line = re.sub(long_number, '(num)', filtered_line) 27 | 28 | sys.stdout.write(filtered_line) 29 | -------------------------------------------------------------------------------- /translate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # to translate interpreter: ./translate.sh interp 4 | # to translate jit : ./translate.sh 5 | if [ -z "$1" ] 6 | then 7 | OPT="-Ojit" 8 | fi 9 | 10 | ../pypy/rpython/bin/rpython $OPT src/main_rpython.py 11 | --------------------------------------------------------------------------------