├── .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: [](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/smarr/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/__init__.py
--------------------------------------------------------------------------------
/src/som/compiler/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarr/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/compiler/__init__.py
--------------------------------------------------------------------------------
/src/som/compiler/ast/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarr/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/smarr/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/smarr/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/interpreter/__init__.py
--------------------------------------------------------------------------------
/src/som/interpreter/ast/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarr/PySOM/25e2f5ab0fa77462f278d8bcbf8976145a939027/src/som/interpreter/ast/__init__.py
--------------------------------------------------------------------------------
/src/som/interpreter/ast/nodes/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/smarr/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/smarr/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/smarr/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/smarr/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/smarr/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/smarr/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/smarr/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/smarr/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/smarr/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/smarr/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 |
--------------------------------------------------------------------------------