├── .github ├── FUNDING.yml └── dependabot.yml ├── .gitignore ├── .replit ├── README.rst ├── build ├── Dockerfile ├── Makefile └── entrypoint.sh ├── compile.py ├── docs ├── progression.tar.lrz ├── self_analogy.png ├── tinySelf_structure.plantuml └── tinySelf_structure.png ├── metadata ├── CHANGELOG.rst ├── LICENSE.txt ├── pypy.revision ├── requirements.packages └── requirements.txt ├── objects └── stdlib.tself ├── run_tests.sh ├── src ├── target.py └── tinySelf │ ├── __init__.py │ ├── config.py │ ├── parser │ ├── __init__.py │ ├── ast_tokens.py │ ├── lexer.py │ └── parser.py │ ├── r_io.py │ ├── shared │ ├── __init__.py │ ├── code_generalization.py │ ├── lightweight_dict.py │ ├── linked_list_for_objects.py │ ├── string_repr.py │ └── two_pointer_array.py │ └── vm │ ├── __init__.py │ ├── bytecodes.py │ ├── code_context.py │ ├── debug │ ├── __init__.py │ ├── plantuml_composer.py │ ├── pretty_print_ast.py │ └── visualisations.py │ ├── frames.py │ ├── interpreter.py │ ├── object_layout.py │ ├── primitives │ ├── __init__.py │ ├── add_primitive_fn.py │ ├── block_traits.py │ ├── cache.py │ ├── interpreter_primitives.py │ ├── mirror.py │ ├── os │ │ ├── __init__.py │ │ ├── primitive_files.py │ │ └── primitive_socket.py │ ├── primitive_dict.py │ ├── primitive_false.py │ ├── primitive_float.py │ ├── primitive_int.py │ ├── primitive_list.py │ ├── primitive_nil.py │ ├── primitive_str.py │ ├── primitive_time.py │ └── primitive_true.py │ └── virtual_machine.py ├── tests ├── jit.py ├── scripts │ ├── count_to_1000000.self │ ├── count_to_500.self │ ├── to_test_include.txt │ └── unittest.self ├── shared │ ├── __init__.py │ ├── test_lightweight_dict.py │ ├── test_linked_list_for_objects.py │ ├── test_string_repr.py │ └── test_two_pointer_array.py ├── test_compiler.py ├── test_lexer.py ├── test_parser.py └── vm │ ├── debug │ └── test_pretty_print_ast.py │ ├── primitives │ ├── test_mirror.py │ ├── test_primitive_false.py │ ├── test_primitive_float.py │ ├── test_primitive_int.py │ ├── test_primitive_nil.py │ └── test_primitive_true.py │ ├── test_frames.py │ ├── test_interpreter.py │ ├── test_object_layout_object.py │ └── test_object_layout_object_map.py └── utils ├── debug_dump.py └── open_windows.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: Bystroushaak 4 | patreon: Bystroushaak 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with a single custom sponsorship URL 13 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: pip 4 | directory: "/metadata" 5 | schedule: 6 | interval: daily 7 | time: "04:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | develop-eggs/ 12 | dist/ 13 | downloads/ 14 | eggs/ 15 | .eggs/ 16 | lib/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | wheels/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | MANIFEST 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *.cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | .static_storage/ 55 | .media/ 56 | local_settings.py 57 | 58 | # Flask stuff: 59 | instance/ 60 | .webassets-cache 61 | 62 | # Scrapy stuff: 63 | .scrapy 64 | 65 | # Sphinx documentation 66 | docs/_build/ 67 | 68 | # PyBuilder 69 | target/ 70 | 71 | # Jupyter Notebook 72 | .ipynb_checkpoints 73 | 74 | # pyenv 75 | .python-version 76 | 77 | # celery beat schedule file 78 | celerybeat-schedule 79 | 80 | # SageMath parsed files 81 | *.sage.py 82 | 83 | # Environments 84 | .env 85 | .venv 86 | env/ 87 | venv/ 88 | ENV/ 89 | env.bak/ 90 | venv.bak/ 91 | 92 | # Spyder project settings 93 | .spyderproject 94 | .spyproject 95 | 96 | # Rope project settings 97 | .ropeproject 98 | 99 | # mkdocs documentation 100 | /site 101 | 102 | # mypy 103 | .mypy_cache/ 104 | 105 | target-c 106 | papers/ 107 | tSelf 108 | 109 | *.code-workspace 110 | .pytest_cache/* 111 | .vscode/* 112 | .history 113 | /*.plantuml 114 | plantuml.jar 115 | frame*.png 116 | parent_map*.png 117 | callgrind* 118 | 119 | .idea 120 | docs/message_send.plantuml -------------------------------------------------------------------------------- /.replit: -------------------------------------------------------------------------------- 1 | language = "python" 2 | run = "src/target.py" -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://api.codacy.com/project/badge/Grade/14d07be60e7d4ae393638b8a87bc3de4 2 | :alt: Codacy Badge 3 | :target: https://app.codacy.com/app/Bystroushaak/tinySelf?utm_source=github.com&utm_medium=referral&utm_content=Bystroushaak/tinySelf&utm_campaign=badger 4 | 5 | [![Run on Repl.it](https://repl.it/badge/github/Bystroushaak/tinySelf)](https://repl.it/github/Bystroushaak/tinySelf) 6 | 7 | tinySelf is an experimental programming language inspired by the `Self lang `_, with the emphasis on the word *experimental*. 8 | 9 | I would like something like the glasswork rack you know from the chemistry pictures, but for computation. 10 | 11 | .. image:: docs/self_analogy.png 12 | 13 | The point of the experiment is not in the language itself, but in all kind of different stuff that can be done with it. tinySelf should be lightweight, compact, collapsible, interchangeable, universal computational apparatus for anything I can possibly hope to make. 14 | 15 | Articles 16 | ++++++++ 17 | 18 | * `Github Wiki `_ 19 | * `Articles about tinySelf `_ 20 | 21 | Planning 22 | ++++++++ 23 | 24 | See 25 | 26 | * https://github.com/Bystroushaak/tinySelf/projects/1 27 | * https://github.com/Bystroushaak/tinySelf/projects/2 28 | 29 | Progress towards the first beta version: 30 | 31 | .. image:: http://kitakitsune.org/sync/visualisations/tinySelf/tinySelf_todo.jpg? 32 | :target: http://kitakitsune.org/sync/visualisations/tinySelf/tinySelf_todo.jpg 33 | -------------------------------------------------------------------------------- /build/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:19.04 2 | 3 | VOLUME /build 4 | VOLUME /release 5 | 6 | USER root 7 | 8 | RUN apt-get update && apt-get install -y \ 9 | git \ 10 | mercurial \ 11 | build-essential \ 12 | pkg-config \ 13 | gcc \ 14 | devscripts \ 15 | fakeroot \ 16 | debhelper \ 17 | curl \ 18 | libffi-dev \ 19 | libffi6 \ 20 | pypy 21 | 22 | RUN git clone https://github.com/Bystroushaak/tinySelf.git /src && \ 23 | hg clone https://bitbucket.org/pypy/pypy /pypy && \ 24 | curl https://bootstrap.pypa.io/get-pip.py | pypy - --user && \ 25 | curl https://bootstrap.pypa.io/get-pip.py | python - --user && \ 26 | pypy -m pip install --user -U git+https://github.com/alex/rply.git && \ 27 | python -m pip install -r /src/metadata/requirements.txt 28 | 29 | COPY ./entrypoint.sh /entrypoint.sh 30 | RUN chmod +x /entrypoint.sh 31 | 32 | WORKDIR /src 33 | 34 | ENTRYPOINT ["/entrypoint.sh"] 35 | -------------------------------------------------------------------------------- /build/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all docker_build 2 | 3 | all: run 4 | 5 | docker_build: test_docker 6 | mkdir -p build 7 | mkdir -p release 8 | chmod 777 build 9 | chmod 777 release 10 | docker build -t tinyself_ubuntu . 11 | 12 | run: docker_build 13 | docker run -ti --rm \ 14 | -e EXTRA_ARGS="-O 3" \ 15 | -e DISPLAY=$DISPLAY \ 16 | -e LC_CTYPE=en_US.UTF-8 \ 17 | -v $(shell echo `pwd`/build):/build \ 18 | -v $(shell echo `pwd`/release):/release \ 19 | --network="host" \ 20 | tinyself_ubuntu 21 | chmod -R 777 release 22 | 23 | jit: docker_build 24 | docker run -ti --rm \ 25 | -e EXTRA_ARGS="--jit -O 3" \ 26 | -e LC_CTYPE=en_US.UTF-8 \ 27 | -v $(shell echo `pwd`/build):/build \ 28 | -v $(shell echo `pwd`/release):/release \ 29 | --network="host" \ 30 | tinyself_ubuntu 31 | chmod -R 777 release 32 | 33 | test_docker: 34 | docker 1>/dev/null 2>&1 || (echo "\033[0;31mDocker not found.\033[0m"; exit 1) 35 | 36 | help: 37 | @echo "Parameters:" 38 | @echo "\trun" 39 | @echo "\t\tFor default build." 40 | @echo "\tjit" 41 | @echo "\t\tFor default build with --jit." 42 | -------------------------------------------------------------------------------- /build/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Inspired by the https://xan-manning.co.uk/making-deb-packages-using-docker/ 3 | 4 | # Clear out the /build and /release directory 5 | rm -rf /build/* 6 | rm -rf /release/* 7 | 8 | cd /pypy 9 | hg update 97567 10 | 11 | cd /src 12 | # Re-pull the repository 13 | git fetch 14 | git pull 15 | 16 | # BUILD_VERSION=$(git describe --tags $(git rev-list --tags --max-count=1)) && \ 17 | # git checkout ${BUILD_VERSION} 18 | BUILD_VERSION=`date "+%Y%m%d%H%M"` 19 | 20 | # Compile tinySelf 21 | export RPYTHON_PATH="/pypy/rpython/bin" 22 | export PYTHON_PATH="src/:/pypy:$PYTHONPATH" 23 | 24 | ./compile.py -q $EXTRA_ARGS 25 | 26 | # copy output binary file 27 | mkdir -p /build/usr/bin 28 | cp tSelf /build/usr/bin 29 | cp tSelf /release 30 | 31 | # copy stdlib 32 | mkdir -p /build/var/lib/tinySelf/ 33 | cp objects/stdlib.tself /build/var/lib/tinySelf/stdlib.tself 34 | 35 | # Get the Install Size 36 | INSTALL_SIZE=$(du -s /build | awk '{ print $1 }') 37 | 38 | # Make DEBIAN directory in /build 39 | mkdir -p /build/DEBIAN 40 | 41 | # Copy the control file from resources 42 | cat > /build/DEBIAN/control < 50 | Description: Programming language inspired by Self. 51 | Self-like language implemented in the RPython language toolkit. 52 | . 53 | 54 | EOF 55 | 56 | # Fill in the information in the control file 57 | sed -i "s/__VERSION__/${BUILD_VERSION}/g" /build/DEBIAN/control 58 | sed -i "s/__FILESIZE__/${INSTALL_SIZE}/g" /build/DEBIAN/control 59 | 60 | # Build our Debian package 61 | fakeroot dpkg-deb -b "/build" 62 | 63 | # Move it to release 64 | mv /build.deb /release/tSelf-${BUILD_VERSION}-amd64.deb 65 | -------------------------------------------------------------------------------- /compile.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python2 2 | # -*- coding: utf-8 -*- 3 | import os 4 | import sys 5 | import os.path 6 | import argparse 7 | 8 | import sh 9 | 10 | 11 | def compile_project(quit_pdb, optimize, jit, debug, output): 12 | target_path = os.path.join( 13 | os.path.dirname(__file__), 14 | "src/target.py" 15 | ) 16 | args = { 17 | "opt": optimize, 18 | "gc": "incminimark", 19 | "output": output, 20 | } 21 | 22 | if jit: 23 | args["translation-jit"] = True 24 | # args["translation-jit_profiler"] = True 25 | 26 | if debug: 27 | args["lldebug"] = True 28 | args["lldebug0"] = True 29 | 30 | if quit_pdb: 31 | args["batch"] = True 32 | 33 | rpython_path = "rpython" 34 | if "RPYTHON_PATH" in os.environ: 35 | rpython_path = os.path.join( 36 | os.environ["RPYTHON_PATH"], 37 | "rpython" 38 | ) 39 | 40 | try: 41 | rpython = sh.Command(rpython_path) 42 | except sh.CommandNotFound: 43 | raise ValueError( 44 | "rpython not found!\n\nPut it into $PATH or use $RPYTHON_PATH env " 45 | "variable to specify it." 46 | ) 47 | 48 | try: 49 | rpython(args, target_path, _fg=True) 50 | except sh.ErrorReturnCode_1: 51 | pass 52 | 53 | 54 | if __name__ == '__main__': 55 | parser = argparse.ArgumentParser() 56 | parser.add_argument( 57 | "-q", 58 | "--quit-pdb", 59 | action="store_true", 60 | help=( 61 | "Jump out of the PDB shell in case there was an error in " 62 | "compilation." 63 | ) 64 | ) 65 | default_level = 1 66 | parser.add_argument( 67 | "-O", 68 | "--optimize", 69 | default=default_level, 70 | metavar="LEVEL", 71 | type=int, 72 | help="Level of optimization. Default %d." % default_level 73 | ) 74 | default_name = "tSelf" 75 | parser.add_argument( 76 | "-r", 77 | "--output", 78 | default=default_name, 79 | metavar="NAME", 80 | help="Name of the output file. Default %s." % default_name 81 | ) 82 | parser.add_argument( 83 | "-j", 84 | "--jit", 85 | action="store_true", 86 | help="Add support for JIT. Warning: really, slow compilation." 87 | ) 88 | parser.add_argument( 89 | "-d", 90 | "--debug", 91 | action="store_true", 92 | help="Add debug informations into the binary." 93 | ) 94 | 95 | args = parser.parse_args() 96 | 97 | try: 98 | compile_project( 99 | args.quit_pdb, 100 | args.optimize, 101 | args.jit, 102 | args.debug, 103 | args.output, 104 | ) 105 | except Exception as e: 106 | sys.stderr.write(e.message + "\n") 107 | sys.exit(1) 108 | -------------------------------------------------------------------------------- /docs/progression.tar.lrz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bystroushaak/tinySelf/749e0f2b269d439b4d3477010463ef921548d5f6/docs/progression.tar.lrz -------------------------------------------------------------------------------- /docs/self_analogy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bystroushaak/tinySelf/749e0f2b269d439b4d3477010463ef921548d5f6/docs/self_analogy.png -------------------------------------------------------------------------------- /docs/tinySelf_structure.plantuml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | object SourceCode { 4 | } 5 | 6 | package Parser as "tinySelf.parser" { 7 | 8 | object Lexer { 9 | } 10 | 11 | object Parser { 12 | } 13 | 14 | object Compiler { 15 | } 16 | } 17 | 18 | 19 | object Stdlib { 20 | } 21 | 22 | package VM as "tinySelf.vm" { 23 | object Interpreter { 24 | } 25 | 26 | object Bytecodes { 27 | } 28 | 29 | object CodeContext { 30 | } 31 | 32 | object Frames { 33 | } 34 | 35 | object ObjectLayout { 36 | } 37 | 38 | object VirtualMachine { 39 | } 40 | 41 | Primitives .u..> Interpreter: is used 42 | Bytecodes .l.> Interpreter: is used 43 | Bytecodes .u..> Compiler: is used 44 | 45 | CodeContext .u.> Interpreter 46 | Frames .u.> Interpreter 47 | ObjectLayout .u.> Interpreter 48 | 49 | Stdlib -r--> VirtualMachine 50 | Stdlib -u[hidden]- SourceCode 51 | 52 | package Primitives as "tinySelf.vm.primitives " { 53 | object Primitives { 54 | } 55 | object PrimitiveInt { 56 | } 57 | object PrimitiveFloat { 58 | } 59 | object PrimitiveStr { 60 | } 61 | object PrimitiveNil { 62 | } 63 | object PrimitiveTrue { 64 | } 65 | object PrimitiveFalse { 66 | } 67 | object PrimitiveList { 68 | } 69 | object PrimitiveTime { 70 | } 71 | object Mirrors { 72 | } 73 | object InterpreterPrimitives { 74 | } 75 | 76 | PrimitiveInt -u-> Primitives 77 | PrimitiveFloat -u-> Primitives 78 | PrimitiveStr -u-> Primitives 79 | PrimitiveNil -u--> Primitives 80 | PrimitiveTrue -u--> Primitives 81 | PrimitiveFalse -u--> Primitives 82 | PrimitiveList -u--> Primitives 83 | PrimitiveTime -u---> Primitives 84 | Mirrors -u---> Primitives 85 | InterpreterPrimitives -u---> Primitives 86 | } 87 | } 88 | 89 | SourceCode -r-> Lexer: characters 90 | Lexer -r-> Parser: tokens 91 | Parser -r-> Compiler: AST 92 | 93 | Compiler -d-> VirtualMachine: bytecode 94 | VirtualMachine -d-> Interpreter 95 | 96 | @enduml -------------------------------------------------------------------------------- /docs/tinySelf_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bystroushaak/tinySelf/749e0f2b269d439b4d3477010463ef921548d5f6/docs/tinySelf_structure.png -------------------------------------------------------------------------------- /metadata/CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | v0.2.0 5 | ------ 6 | - Added blocks. 7 | - Added resends. 8 | - RPython compilation check. 9 | 10 | v0.1.0 11 | ------ 12 | - First non-trivial expression is interpreted correctly: ``(| a <- 1. add: b = (|| (a + 1) + b) |) add: 2`` results to 4. 13 | -------------------------------------------------------------------------------- /metadata/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Bystroushaak 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /metadata/pypy.revision: -------------------------------------------------------------------------------- 1 | 96452 -------------------------------------------------------------------------------- /metadata/requirements.packages: -------------------------------------------------------------------------------- 1 | gcc 2 | libffi-dev 3 | -------------------------------------------------------------------------------- /metadata/requirements.txt: -------------------------------------------------------------------------------- 1 | sh 2 | rply -------------------------------------------------------------------------------- /objects/stdlib.tself: -------------------------------------------------------------------------------- 1 | (| 2 | init_true = (| true_mirror | 3 | true_mirror: primitives mirrorOn: true. 4 | 5 | true_mirror toSlot: 'not' Add: (|| false). 6 | true_mirror toSlot: 'ifTrue:' Add: (| :blck | blck value). 7 | true_mirror toSlot: 'ifFalse:' Add: (| :blck | nil.). 8 | true_mirror toSlot: 'ifTrue:False:' Add: (| :true_blck. :false_blck | 9 | true_blck value 10 | ). 11 | true_mirror toSlot: 'ifFalse:True:' Add: (| :false_blck. :true_blck | 12 | true_blck value. 13 | ). 14 | true_mirror toSlot: 'print' Add: (|| 15 | "true" print. 16 | ). 17 | true_mirror toSlot: 'printLine' Add: (|| 18 | "true" printLine. 19 | ). 20 | true_mirror toSlot: 'asBool' Add: (|| 21 | ^ self. 22 | ). 23 | true_mirror toSlot: 'asString' Add: (|| 24 | "true". 25 | ). 26 | true_mirror toSlot: '==' Add: (| :other | 27 | self is: other asBool. 28 | ). 29 | ). 30 | 31 | init_false = (| false_mirror | 32 | false_mirror: primitives mirrorOn: false. 33 | 34 | false_mirror toSlot: 'not' Add: (| | true). 35 | false_mirror toSlot: 'ifTrue:' Add: (| :blck | nil). 36 | false_mirror toSlot: 'ifFalse:' Add: (| :blck | blck value.). 37 | false_mirror toSlot: 'ifTrue:False:' Add: (| :true_blck. :false_blck | 38 | false_blck value 39 | ). 40 | false_mirror toSlot: 'ifFalse:True:' Add: (| :false_blck. :true_blck | 41 | false_blck value. 42 | ). 43 | false_mirror toSlot: 'print' Add: (|| 44 | "false" print. 45 | ). 46 | false_mirror toSlot: 'printLine' Add: (|| 47 | "false" printLine. 48 | ). 49 | false_mirror toSlot: 'asBool' Add: (|| 50 | self. 51 | ). 52 | false_mirror toSlot: 'asString' Add: (|| 53 | "false". 54 | ). 55 | false_mirror toSlot: '==' Add: (| :other | 56 | self is: other asBool. 57 | ). 58 | ). 59 | 60 | init_nil = (| nil_mirror | 61 | nil_mirror: primitives mirrorOn: nil. 62 | 63 | nil_mirror toSlot: 'asString' Add: (|| 64 | "nil". 65 | ). 66 | nil_mirror toSlot: 'print' Add: (|| 67 | "nil" print. 68 | ). 69 | nil_mirror toSlot: 'printLine' Add: (|| 70 | "nil" printLine. 71 | ). 72 | nil_mirror toSlot: 'asBool' Add: (|| 73 | false. 74 | ). 75 | nil_mirror toSlot: '==' Add: (| :other | 76 | self is: other. 77 | ). 78 | ). 79 | 80 | init_int = (| int_mirror | 81 | int_mirror: primitives mirrorOn: traits int. 82 | ). 83 | 84 | init_float = (| float_mirror | 85 | float_mirror: primitives mirrorOn: traits float. 86 | 87 | float_mirror toSlot: 'print' Add: (|| 88 | self asString print. 89 | self asString. 90 | ). 91 | float_mirror toSlot: 'printLine' Add: (|| 92 | (self asString + '\n') print. 93 | (self asString + '\n') 94 | ). 95 | float_mirror toSlot: 'asBool' Add: (|| 96 | (self == 0.0) ifTrue: [^false]. 97 | true. 98 | ). 99 | ). 100 | 101 | init_str = (| str_mirror | 102 | str_mirror: primitives mirrorOn: traits str. 103 | ). 104 | 105 | init_asserts = (| universe_mirror | 106 | universe_mirror: primitives mirrorOn: universe. 107 | universe_mirror toSlot: 'assert:' Add: (| :what | 108 | what value ifFalse: [ 109 | primitives interpreter error: ( 110 | '`assert:` failed (line ' + (what getLineNumber asString) + '):\n\n\t' + (what asString) 111 | ) 112 | ]. 113 | true. 114 | ). 115 | universe_mirror toSlot: 'assertNot:' Add: (| :what | 116 | what value ifTrue: [ 117 | primitives interpreter error: ( 118 | '`assertNot:` failed (line ' + (what getLineNumber asString) + '):\n\n\t' + (what asString) 119 | ) 120 | ]. 121 | true. 122 | ). 123 | ). 124 | 125 | init_blocks = (| block_traits_mirror | 126 | block_traits_mirror: primitives mirrorOn: traits block. 127 | block_traits_mirror toSlot: 'whileTrue:' Add: (| :blck | 128 | self value ifFalse: [ ^nil ]. 129 | blck value. 130 | ^self whileTrue: blck. 131 | ). 132 | block_traits_mirror toSlot: 'whileFalse:' Add: (| :blck | 133 | self value ifTrue: [ ^nil ]. 134 | blck value. 135 | ^self whileFalse: blck. 136 | ). 137 | block_traits_mirror toSlot: 'untilTrue:' Add: (| :blck | 138 | blck value. 139 | self value ifTrue: [ ^nil ]. 140 | ^self untilTrue: blck. 141 | ). 142 | block_traits_mirror toSlot: 'untilFalse:' Add: (| :blck | 143 | blck value. 144 | self value ifFalse: [ ^nil ]. 145 | ^self untilFalse: blck. 146 | ). 147 | ). 148 | 149 | init_dicts = (| traits_mirror. dict_mirror. | 150 | traits_mirror: primitives mirrorOn: traits. 151 | traits_mirror toSlot: 'dict' Add: (| 152 | at: key Fail: fail_blck = (| result | 153 | result: (self at: key). 154 | (result is: nil) ifTrue: [ ^ fail_blck value ]. 155 | ^result. 156 | ). 157 | |). 158 | 159 | dict_mirror: primitives mirrorOn: dict. 160 | dict_mirror toParent: 'traits' Add: traits dict. 161 | ). 162 | 163 | init_lists = (| list_mirror. traits_mirror. | 164 | traits_mirror: primitives mirrorOn: traits. 165 | traits_mirror toSlot: 'list' Add: (| 166 | do: blk = (| size. cnt. | 167 | cnt: 0. 168 | size: self length. 169 | 170 | [cnt < size] whileTrue: [ 171 | blk with: (self at: cnt) With: cnt. 172 | cnt: cnt + 1. 173 | ]. 174 | ). 175 | 176 | == other = (|| 177 | primitives interpreter setErrorHandler: [:msg. :error_process | 178 | (primitives interpreter) restoreProcess: error_process With: false. 179 | ]. 180 | 181 | ((self length) == (other length)) ifFalse: [ ^false ]. 182 | 183 | self do: [| :item. :index. | 184 | ((other at: index) == item) ifFalse: [ ^false ]. 185 | ]. 186 | 187 | ^ true. 188 | ). 189 | 190 | asBool = (|| 191 | (self length) == 0 ifTrue: [ ^ false ]. 192 | ^ true. 193 | ). 194 | |). 195 | 196 | list_mirror: primitives mirrorOn: list. 197 | list_mirror toParent: 'traits' Add: traits list. 198 | ). 199 | 200 | init_files = (| file_mirror | 201 | file_mirror: primitives mirrorOn: primitives os files. 202 | 203 | file_mirror toSlot: 'openForAppend:' Add: (| :fn | 204 | primitives os files open: fn Mode: "a" Fails: [| :err | 205 | raise: err. 206 | ]. 207 | ). 208 | file_mirror toSlot: 'openForWrite:' Add: (| :fn | 209 | primitives os files open: fn Mode: "w" Fails: [| :err | 210 | raise: err. 211 | ]. 212 | ). 213 | ). 214 | 215 | init = (|| 216 | init_true. 217 | init_false. 218 | init_nil. 219 | init_int. 220 | init_float. 221 | init_str. 222 | init_asserts. 223 | init_blocks. 224 | init_dicts. 225 | init_lists. 226 | init_files. 227 | ). 228 | |) init. -------------------------------------------------------------------------------- /run_tests.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | export PYTHONPATH="src/:$PYTHONPATH" 3 | 4 | if [ -e "tests/__pycache__" ]; then 5 | rm -fr "tests/__pycache__" 6 | fi 7 | 8 | if [ -e "tests/vm/__pycache__" ]; then 9 | rm -fr "tests/vm/__pycache__" 10 | fi 11 | 12 | python2 -m py.test tests $@ 13 | -------------------------------------------------------------------------------- /src/target.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python2 2 | # -*- coding: utf-8 -*- 3 | import os 4 | import os.path 5 | 6 | from rply import ParsingError 7 | from rpython.rlib.objectmodel import we_are_translated 8 | from rpython.rlib.compilerinfo import get_compiler_info 9 | from rpython.jit.codewriter.policy import JitPolicy 10 | 11 | from tinySelf.r_io import ewrite 12 | from tinySelf.r_io import writeln 13 | from tinySelf.r_io import ewriteln 14 | from tinySelf.r_io import stdin_readline 15 | 16 | from tinySelf.config import VERSION 17 | from tinySelf.config import STDLIB_PATHS 18 | 19 | from tinySelf.parser import lex_and_parse 20 | from tinySelf.parser import lex_and_parse_as_root 21 | 22 | from tinySelf.vm.primitives import PrimitiveNilObject 23 | from tinySelf.vm.code_context import CodeContext 24 | from tinySelf.vm.virtual_machine import virtual_machine 25 | 26 | from tinySelf.shared.string_repr import unescape_esc_seq 27 | 28 | 29 | NIL = PrimitiveNilObject() 30 | 31 | 32 | def run_interactive(): 33 | _, interpreter = virtual_machine("(||)", "-", *_read_stdlib()) 34 | 35 | while True: 36 | line = stdin_readline(":> ") 37 | 38 | if len(line) == 0 or line.strip() == "exit": 39 | writeln() 40 | return 0 41 | 42 | line = "(| run = (||\n" + line + "\n) |) run" 43 | 44 | try: 45 | for expr in lex_and_parse(line): 46 | code = expr.compile(CodeContext()) 47 | process = interpreter.add_process(code.finalize()) 48 | interpreter.interpret() 49 | 50 | if process.finished_with_error: 51 | ewriteln("Error:" + process.result.__str__()) 52 | else: 53 | ewriteln(process.result.__str__()) 54 | 55 | except ParsingError as e: 56 | ewriteln("Parse error.") 57 | if e.message: 58 | ewriteln(e.message) 59 | continue 60 | 61 | return 0 62 | 63 | 64 | def run_script(path): 65 | with open(path) as f: 66 | process, interpreter = virtual_machine(f.read(), path, *_read_stdlib()) 67 | 68 | if process.finished_with_error: 69 | ewrite("Error: ") 70 | ewriteln(unescape_esc_seq(process.result.__str__())) 71 | 72 | if not "disassembled" in process.result.__str__(): 73 | # TODO: can this even happen? 74 | ewriteln("\n") 75 | ewriteln("CodeContext debug:") 76 | ewriteln(process.frame.code_context.debug_repr()) 77 | 78 | return 1 79 | 80 | return 0 81 | 82 | 83 | def _read_stdlib(): 84 | """ 85 | Return tuple with content of stdlib file and path to it. 86 | """ 87 | tinyself_path_var = os.environ.get("TINYSELF_PATH") 88 | 89 | # I would use .get() with alt param, but that results in Argument number 90 | # mismatch under RPython 91 | if not tinyself_path_var: 92 | tinyself_path_var = STDLIB_PATHS 93 | 94 | stdlib_paths = tinyself_path_var.split(":") 95 | 96 | for path in stdlib_paths: 97 | if not os.path.exists(path): 98 | continue 99 | 100 | with open(path) as f: 101 | return f.read(), path 102 | 103 | ewriteln("Can't read stdlib, no `TINYSELF_PATH` env variable given.") 104 | return "", "" 105 | 106 | 107 | def show_ast(path): 108 | with open(path) as f: 109 | expressions = lex_and_parse(f.read()) 110 | 111 | if not we_are_translated(): 112 | from tinySelf.vm.debug.pretty_print_ast import pretty_print_ast 113 | 114 | try: 115 | for expr in expressions: 116 | if we_are_translated(): 117 | writeln(expr.__str__()) 118 | else: 119 | writeln(pretty_print_ast(expr.__str__())) 120 | 121 | except ParsingError as e: 122 | ewriteln("Parse error.") 123 | if e.message: 124 | ewriteln(e.message) 125 | return 1 126 | 127 | return 0 128 | 129 | 130 | def compile_file(path): 131 | with open(path) as f: 132 | try: 133 | ast = lex_and_parse_as_root(f.read()) 134 | code = ast.compile(CodeContext()) 135 | code.finalize() 136 | except ParsingError as e: 137 | ewriteln("Parse error.") 138 | if e.message: 139 | ewriteln(e.message) 140 | return 1 141 | 142 | writeln(code.debug_repr()) 143 | 144 | return 0 145 | 146 | 147 | # def run_snapshot(path): 148 | # print "Running snapshot", path 149 | # return 0 150 | 151 | 152 | def print_help(fn): 153 | ewriteln("Usage:") 154 | ewriteln("\t%s [-h, -v] [-f FN] [-c FN] [-a FN]" % fn) 155 | ewriteln("") 156 | ewriteln("\t-f FN, --filename FN") 157 | ewriteln("\t\tRun `FN` as a tinySelf script.") 158 | ewriteln("") 159 | # ewriteln("\t-s FN, --snapshot FN") 160 | # ewriteln("\t\tRun memory snapshot `FN`.") 161 | # ewriteln("") 162 | ewriteln("\t-h, --help") 163 | ewriteln("\t\tShow this help.") 164 | ewriteln("") 165 | ewriteln("\t-v, --version") 166 | ewriteln("\t\tShow version and compiler information.") 167 | ewriteln("") 168 | ewriteln("\tSCRIPT_PATH") 169 | ewriteln("\t\tRun script `SCRIPT_PATH`.") 170 | ewriteln("") 171 | ewriteln("Debug options:") 172 | ewriteln("") 173 | ewriteln("\t-c FN, --compile FN") 174 | ewriteln("\t\tCompile FN, output bytecode to the stdout.") 175 | ewriteln("") 176 | ewriteln("\t-a FN, --ast FN") 177 | ewriteln("\t\tShow AST of the `FN`.") 178 | ewriteln("") 179 | 180 | 181 | def parse_arg_with_file_param(command, path): 182 | if not os.path.exists(path): 183 | ewriteln("`%s` not found!\n" % path) 184 | return 1 185 | 186 | if command in ["-a", "--ast"]: 187 | return show_ast(path) 188 | elif command in ["-f", "--filename"]: 189 | return run_script(path) 190 | elif command in ["-c", "--compile"]: 191 | return compile_file(path) 192 | # elif command in ["-s", "--snapshot"]: 193 | # return run_script(path) 194 | 195 | ewriteln("Unknown command `%s`!" % command) 196 | return 1 197 | 198 | 199 | def parse_args(argv): 200 | if len(argv) == 1: 201 | return run_interactive() 202 | 203 | elif len(argv) == 2: 204 | if argv[1] in ["-h", "--help"]: 205 | print_help(argv[0]) 206 | return 0 207 | 208 | if argv[1] in ["-v", "--version"]: 209 | writeln("tSelf " + VERSION) 210 | writeln(get_compiler_info()) 211 | return 0 212 | 213 | elif argv[1].startswith("-"): 214 | ewriteln( 215 | "%s probably requires a parameter. See --help for details!" % argv[1] 216 | ) 217 | return 1 218 | 219 | elif not os.path.exists(argv[1]): 220 | ewriteln("Unrecognized option `%s`!" % argv[1]) 221 | return 1 222 | 223 | else: 224 | return run_script(argv[1]) 225 | 226 | elif len(argv) == 3: 227 | return parse_arg_with_file_param(argv[1], argv[2]) 228 | 229 | else: 230 | ewriteln("Unknown arguments `%s`!" % str(argv[1:])) 231 | return 1 232 | 233 | 234 | def main(argv): 235 | return parse_args(argv) 236 | 237 | 238 | def target(driver, args): 239 | return main, None 240 | 241 | 242 | def jitpolicy(driver): 243 | return JitPolicy() 244 | 245 | 246 | def untranslated_main(): 247 | """ 248 | Runs main, exiting with the appropriate exit status afterwards. 249 | """ 250 | import sys 251 | sys.exit(main(sys.argv)) 252 | 253 | 254 | if __name__ == '__main__': 255 | untranslated_main() 256 | -------------------------------------------------------------------------------- /src/tinySelf/__init__.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python2 2 | # -*- coding: utf-8 -*- 3 | # 4 | -------------------------------------------------------------------------------- /src/tinySelf/config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | VERSION = "0.0.1" 4 | 5 | # Use MethodStack based on LinkedList (=True) or PreallocatedArray (=False)? 6 | # LinkedList is faster with JIT, and PreallocatedArray without it. 7 | USE_LINKED_LIST_METHOD_STACK = True 8 | 9 | STDLIB_PATHS = "objects/stdlib.tself:/var/lib/tinySelf/stdlib.tself" -------------------------------------------------------------------------------- /src/tinySelf/parser/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from parser import lex_and_parse 3 | from parser import lex_and_parse_as_root 4 | -------------------------------------------------------------------------------- /src/tinySelf/parser/lexer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from rply import LexerGenerator 3 | 4 | 5 | lg = LexerGenerator() 6 | lg.ignore(r'\s+') 7 | 8 | lg.add('SELF', r'self') 9 | 10 | lg.add('NUMBER', r'(0x[0-9a-fA-F]+)|((\-)?\d+(\.\d+)?)') 11 | 12 | lg.add('OBJ_START', r'\(') 13 | lg.add('OBJ_END', r'\)') 14 | 15 | lg.add('BLOCK_START', r'\[') 16 | lg.add('BLOCK_END', r'\]') 17 | 18 | lg.add('SINGLE_Q_STRING', r"'(?:\\.|[^'\\])*'") 19 | lg.add('DOUBLE_Q_STRING', r'"(?:\\.|[^"\\])*"') 20 | 21 | lg.add('FIRST_KW', r'([a-z_][a-zA-Z0-9_]*\.)*[a-z_]+[a-zA-Z0-9_]*:') 22 | lg.add('KEYWORD', r'[A-Z]+[a-zA-Z0-9_]*:') 23 | lg.add('ARGUMENT', r':[a-zA-Z_]*[a-zA-Z0-9_]+') 24 | 25 | lg.add('RW_ASSIGNMENT', r'\<-') 26 | 27 | lg.add('OPERATOR', r'(\!\=+)|([!@\$%&\*\-\+~/?<>,]+)|(==+)') 28 | lg.add('RETURN', r'\^') 29 | lg.add('END_OF_EXPR', r'\.') 30 | lg.add('SEPARATOR', r'\|') 31 | lg.add('CASCADE', r'\;') 32 | 33 | lg.add('IDENTIFIER', r'([a-zA-Z_][a-zA-Z0-9_]*\.)*[a-zA-Z_]*[a-zA-Z0-9_\*\?]+') 34 | 35 | lg.add('ASSIGNMENT', r'=') 36 | 37 | lg.add('COMMENT', r'#.*[\n|$]?') 38 | 39 | 40 | lexer = lg.build() 41 | -------------------------------------------------------------------------------- /src/tinySelf/r_io.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os 3 | 4 | 5 | def write(msg): 6 | os.write(1, msg or "") 7 | 8 | 9 | def writeln(msg=""): 10 | os.write(1, msg or "") 11 | os.write(1, "\n") 12 | 13 | 14 | def ewrite(msg): 15 | os.write(2, msg or "") 16 | 17 | 18 | def ewriteln(msg=""): 19 | os.write(2, msg or "") 20 | os.write(2, "\n") 21 | 22 | 23 | def stdin_readline(prompt=""): 24 | if prompt: 25 | write(prompt) 26 | 27 | line = os.read(0, 65535) 28 | assert isinstance(line, str) 29 | 30 | return line 31 | -------------------------------------------------------------------------------- /src/tinySelf/shared/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /src/tinySelf/shared/code_generalization.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import inspect 3 | 4 | 5 | def create_copy_with_different_types(cls, new_name, other_replacements=None): 6 | """ 7 | Create copy of the `cls` item with `new_name`. Also optionally run 8 | .replace() calls on the source for all pairs in `other_replacements`. 9 | 10 | This function is useful as kind of template for generalization of code. 11 | 12 | For example, when you create a new container object, type annotator assigns 13 | types to each property when it is first used. You can't use it for other 14 | types as well. Other languages (Java, C++) solve this problem by using 15 | templating, but RPython provides no such way, so I am using code generation 16 | on the source level to generate another class with exact same source code, 17 | but different names. 18 | 19 | Args: 20 | cls (class): Some class you wish to copy. 21 | new_name (str): New name for the class. 22 | other_replacements (list of pairs, default None): Other string 23 | replacements. Literally just bunch of .replace() calls for each 24 | pair. 25 | 26 | Returns: 27 | str: Modified source. Use exec .. to generate code on the fly. 28 | """ 29 | source = "".join(inspect.getsourcelines(cls)[0]) 30 | source = source.replace(cls.__name__, new_name) 31 | 32 | if other_replacements is not None: 33 | for item in other_replacements: 34 | source = source.replace(*item) 35 | 36 | return source 37 | -------------------------------------------------------------------------------- /src/tinySelf/shared/lightweight_dict.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from collections import OrderedDict 3 | 4 | from tinySelf.shared.code_generalization import create_copy_with_different_types 5 | 6 | 7 | class Container(object): 8 | def __init__(self, val): 9 | self.val = val 10 | 11 | def __eq__(self, other): 12 | if isinstance(other, Container): 13 | return self.val == other.val 14 | 15 | return False 16 | 17 | 18 | class LightWeightDict(object): 19 | """ 20 | Implementation with just properties or dict. 21 | """ 22 | def __init__(self): 23 | self._first_key = None 24 | self._first_value = None 25 | self._second_key = None 26 | self._second_value = None 27 | self._third_key = None 28 | self._third_value = None 29 | 30 | self._use_properties = True 31 | self._use_dict = False 32 | 33 | self._dict = None 34 | 35 | def has_key(self, key): 36 | if self._use_properties: 37 | return key == self._first_key or \ 38 | key == self._second_key or \ 39 | key == self._third_key 40 | else: 41 | return key in self._dict 42 | 43 | def __contains__(self, key): 44 | return self.has_key(key) 45 | 46 | def set(self, key, val): 47 | if self._use_properties: 48 | if self._first_key is None or self._first_key == key: 49 | self._first_key = key 50 | self._first_value = Container(val) 51 | elif self._second_key is None or self._second_key == key: 52 | self._second_key = key 53 | self._second_value = Container(val) 54 | elif self._third_key is None or self._third_key == key: 55 | self._third_key = key 56 | self._third_value = Container(val) 57 | else: 58 | self._use_properties = False 59 | self._use_dict = True 60 | self._dict = OrderedDict() 61 | 62 | self._dict[self._first_key] = self._first_value.val 63 | self._dict[self._second_key] = self._second_value.val 64 | self._dict[self._third_key] = self._third_value.val 65 | 66 | self._first_key = None 67 | self._first_value = None 68 | self._second_key = None 69 | self._second_value = None 70 | self._third_key = None 71 | self._third_value = None 72 | 73 | self._dict[key] = val 74 | 75 | else: 76 | self._dict[key] = val 77 | 78 | def __setitem__(self, key, val): 79 | self.set(key, val) 80 | 81 | def get(self, key, alt=None): 82 | if self._use_properties: 83 | if self._first_key == key: 84 | return self._first_value.val 85 | elif self._second_key == key: 86 | return self._second_value.val 87 | elif self._third_key == key: 88 | return self._third_value.val 89 | else: 90 | return alt 91 | else: 92 | return self._dict.get(key, alt) 93 | 94 | def __getitem__(self, key): 95 | if self._use_properties: 96 | if self._first_key == key: 97 | return self._first_value.val 98 | elif self._second_key == key: 99 | return self._second_value.val 100 | elif self._third_key == key: 101 | return self._third_value.val 102 | else: 103 | raise KeyError("`%s` not found." % key) 104 | else: 105 | return self._dict[key] 106 | 107 | def delete(self, key): 108 | if self._use_properties: 109 | if self._first_key == key: 110 | self._first_key = self._second_key 111 | self._second_key = self._third_key 112 | self._third_key = None 113 | 114 | self._first_value = self._second_value 115 | self._second_value = self._third_value 116 | self._third_value = None 117 | 118 | elif self._second_key == key: 119 | self._second_key = self._third_key 120 | self._third_key = None 121 | 122 | self._second_value = self._third_value 123 | self._third_value = None 124 | 125 | elif self._third_key == key: 126 | self._third_key = None 127 | self._third_value = None 128 | 129 | else: 130 | if key in self._dict: 131 | del self._dict[key] 132 | 133 | def __delitem__(self, key): 134 | return self.delete(key) 135 | 136 | def keys(self): 137 | if self._use_properties: 138 | keys = [] 139 | if self._first_key is not None: 140 | keys.append(self._first_key) 141 | if self._second_key is not None: 142 | keys.append(self._second_key) 143 | if self._third_key is not None: 144 | keys.append(self._third_key) 145 | 146 | return keys 147 | 148 | else: 149 | return self._dict.keys() 150 | 151 | def values(self): 152 | if self._use_properties: 153 | values = [] 154 | if self._first_key is not None: 155 | values.append(self._first_value.val) 156 | if self._second_key is not None: 157 | values.append(self._second_value.val) 158 | if self._third_key is not None: 159 | values.append(self._third_value.val) 160 | 161 | return values 162 | 163 | else: 164 | return self._dict.values() 165 | 166 | def __len__(self): 167 | if self._use_properties: 168 | length = 0 169 | if self._first_key is not None: 170 | length += 1 171 | if self._second_key is not None: 172 | length += 1 173 | if self._third_key is not None: 174 | length += 1 175 | 176 | return length 177 | 178 | else: 179 | return len(self._dict) 180 | 181 | def copy(self): 182 | lwd = LightWeightDict() 183 | 184 | if self._use_properties: 185 | lwd._first_key = self._first_key 186 | lwd._first_value = self._first_value 187 | lwd._second_key = self._second_key 188 | lwd._second_value = self._second_value 189 | lwd._third_key = self._third_key 190 | lwd._third_value = self._third_value 191 | else: 192 | lwd._dict = self._dict.copy() 193 | 194 | lwd._use_properties = self._use_properties 195 | lwd._use_dict = self._use_dict 196 | 197 | return lwd 198 | 199 | def __eq__(self, other): 200 | if isinstance(other, LightWeightDict): 201 | if self._use_properties and other._use_properties: 202 | return (self._first_key == other._first_key and 203 | self._first_value == other._first_value and 204 | self._second_key == other._second_key and 205 | self._second_value == other._second_value and 206 | self._third_key == other._third_key and 207 | self._third_value == other._third_value) 208 | 209 | elif self._use_dict and other._use_dict: 210 | return self._dict == other._dict 211 | 212 | else: 213 | if self.keys() != other.keys(): 214 | return False 215 | 216 | for k, v in self.iteritems(): 217 | if other.get(k) != v: 218 | return False 219 | 220 | return True 221 | 222 | elif isinstance(other, dict): 223 | if self._use_dict: 224 | return self._dict == other 225 | 226 | other_lwd = LightWeightDict() 227 | for k, v in other.iteritems(): 228 | other_lwd[k] = v 229 | 230 | return self == other_lwd 231 | 232 | else: 233 | return False 234 | 235 | def __ne__(self, other): 236 | return not self.__eq__(other) 237 | 238 | def iteritems(self): 239 | if self._use_properties: 240 | if self._first_key is not None: 241 | yield self._first_key, self._first_value.val 242 | if self._second_key is not None: 243 | yield self._second_key, self._second_value.val 244 | if self._third_key is not None: 245 | yield self._third_key, self._third_value.val 246 | 247 | else: 248 | for k, v in self._dict.iteritems(): 249 | yield k, v 250 | 251 | 252 | class LightWeightDictObjects(LightWeightDict): 253 | """Just token for IDE rewritten in create_copy_with_different_types()""" 254 | 255 | 256 | exec create_copy_with_different_types(Container, "ObjectContainer") 257 | exec create_copy_with_different_types(LightWeightDict, "LightWeightDictObjects", 258 | [["Container", "ObjectContainer"]]) 259 | -------------------------------------------------------------------------------- /src/tinySelf/shared/linked_list_for_objects.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | class LinkedListBox(object): 4 | def __init__(self, value): 5 | self.value = value 6 | 7 | self._next = None 8 | self._prev = None 9 | 10 | 11 | class LinkedListForObjects(object): 12 | def __init__(self): 13 | self._first_item = None 14 | self._last_item = None 15 | self.length = 0 16 | 17 | def __len__(self): 18 | return self.length 19 | 20 | def __getitem__(self, index): 21 | boxed_item = self._get_boxed_item_on_index(index) 22 | return boxed_item.value 23 | 24 | def __setitem__(self, index, value): 25 | boxed_item = self._get_boxed_item_on_index(index) 26 | boxed_item.value = value 27 | 28 | def _get_boxed_item_on_index(self, index): 29 | if self._first_item is None: 30 | raise IndexError("Invalid index `%s` (empty list)." % index) 31 | 32 | if index + 1 > self.length: 33 | raise IndexError("Invalid index `%s`." % index) 34 | 35 | if index < -1: 36 | raise IndexError("Negative indexes not yet supported!") 37 | 38 | if index == -1: 39 | return self._last_item 40 | elif index == 0: 41 | return self._first_item 42 | 43 | boxed_item = self._first_item._next 44 | for _ in range(index - 1): 45 | boxed_item = boxed_item._next 46 | 47 | return boxed_item 48 | 49 | def pop_first(self): 50 | if self.length == 0: 51 | raise IndexError("pop from empty list") 52 | elif self.length == 1: 53 | first_item_box = self._first_item 54 | 55 | self._first_item = None 56 | self._last_item = None 57 | self.length = 0 58 | 59 | return first_item_box.value 60 | 61 | first_item_box = self._first_item 62 | self._first_item = self._first_item._next 63 | self._first_item._prev = None 64 | self.length -= 1 65 | 66 | return first_item_box.value 67 | 68 | def pop_last(self): 69 | if self.length == 0: 70 | raise IndexError("pop from empty list") 71 | elif self.length == 1: 72 | self.length = 0 73 | last_item_box = self._last_item 74 | 75 | self._first_item = None 76 | self._last_item = None 77 | 78 | return last_item_box.value 79 | 80 | last_item_box = self._last_item 81 | self._last_item = self._last_item._prev 82 | self._last_item._next = None 83 | self.length -= 1 84 | 85 | return last_item_box.value 86 | 87 | def append(self, item): 88 | boxed_item = LinkedListBox(item) 89 | 90 | if self._first_item is None: 91 | self._first_item = boxed_item 92 | self._last_item = boxed_item 93 | self.length += 1 94 | return 95 | 96 | boxed_item._prev = self._last_item 97 | self._last_item._next = boxed_item 98 | self._last_item = boxed_item 99 | 100 | self.length += 1 101 | 102 | def to_list(self): 103 | if self._first_item is None: 104 | return [] 105 | 106 | boxed_item = self._first_item 107 | items = [boxed_item.value] 108 | while boxed_item._next is not None: 109 | boxed_item = boxed_item._next 110 | items.append(boxed_item.value) 111 | 112 | return items 113 | -------------------------------------------------------------------------------- /src/tinySelf/shared/string_repr.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | def unescape_esc_seq(inp): 5 | if len(inp) < 2: 6 | return inp 7 | 8 | out = "" 9 | last = inp[0] 10 | inp = inp[1:] + "-" 11 | 12 | escape = last == "\\" 13 | for c in inp: 14 | if escape: 15 | if c == "n": 16 | last = "\n" 17 | elif c == "t": 18 | last = "\t" 19 | else: 20 | last = c 21 | 22 | out += last 23 | 24 | last = "" if escape else c 25 | escape = not escape if c == "\\" else False 26 | 27 | return out 28 | 29 | 30 | def escape(inp): 31 | out = "" 32 | 33 | for c in inp: 34 | if c == "\n": 35 | out += "\\n" 36 | elif c == "\t": 37 | out += "\\t" 38 | elif c == "\"": 39 | out += "\\\"" 40 | elif c == "'": 41 | out += "\\'" 42 | else: 43 | out += c 44 | 45 | return out 46 | -------------------------------------------------------------------------------- /src/tinySelf/shared/two_pointer_array.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from rpython.rlib import jit 3 | 4 | from tinySelf.shared.code_generalization import create_copy_with_different_types 5 | 6 | 7 | class TwoPointerArray(object): 8 | def __init__(self, length): 9 | self._allocated_length = length 10 | self._array = [None for _ in xrange(length)] 11 | self._left_pointer = 0 12 | self._right_pointer = 0 13 | 14 | @jit.unroll_safe 15 | def reset(self): 16 | while self._left_pointer <= self._right_pointer: 17 | self._array[self._left_pointer] = None 18 | self._left_pointer += 1 19 | 20 | self._left_pointer = 0 21 | self._right_pointer = 0 22 | 23 | return self 24 | 25 | def __len__(self): 26 | return self._right_pointer - self._left_pointer 27 | 28 | def __getitem__(self, index): 29 | if index >= self._right_pointer: 30 | raise IndexError() 31 | 32 | return self._array[self._left_pointer + index] 33 | 34 | def __setitem__(self, index, value): 35 | self._array[index] = value 36 | 37 | def pop_first(self): 38 | if self._left_pointer == self._right_pointer: 39 | raise IndexError() 40 | 41 | rval = self._array[self._left_pointer] 42 | self._array[self._left_pointer] = None 43 | self._left_pointer += 1 44 | return rval 45 | 46 | def pop_last(self): 47 | if self._left_pointer == self._right_pointer: 48 | raise IndexError() 49 | 50 | rval = self._array[self._right_pointer - 1] 51 | self._array[self._right_pointer - 1] = None 52 | self._right_pointer -= 1 53 | return rval 54 | 55 | def append(self, item): 56 | if self._right_pointer >= self._allocated_length: 57 | self._array.append(item) 58 | self._right_pointer += 1 59 | self._allocated_length += 1 60 | return 61 | 62 | self._array[self._right_pointer] = item 63 | self._right_pointer += 1 64 | 65 | def to_list(self): 66 | return self._array[self._left_pointer: self._right_pointer] 67 | 68 | def __iter__(self): 69 | return iter(self.to_list()) 70 | 71 | 72 | class NumericTwoPointerArray(TwoPointerArray): 73 | """Just placeholder for the IDE.""" 74 | 75 | 76 | exec create_copy_with_different_types(TwoPointerArray, "NumericTwoPointerArray", 77 | [["None", "0"]]) 78 | -------------------------------------------------------------------------------- /src/tinySelf/vm/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.vm.object_layout import Object 3 | from tinySelf.vm.object_layout import Block 4 | -------------------------------------------------------------------------------- /src/tinySelf/vm/bytecodes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | BYTECODE_SEND = 0 4 | # BYTECODE_SELF_SEND = 1 5 | BYTECODE_PUSH_SELF = 2 6 | BYTECODE_PUSH_LITERAL = 3 7 | BYTECODE_ADD_SLOT = 4 8 | 9 | # these should always be the last ones for dynamic dispatch 10 | BYTECODE_RETURN_TOP = 5 11 | BYTECODE_RETURN_IMPLICIT = 6 12 | 13 | LITERAL_TYPE_NIL = 0 14 | LITERAL_TYPE_INT = 1 15 | LITERAL_TYPE_STR = 2 16 | LITERAL_TYPE_OBJ = 3 17 | LITERAL_TYPE_FLOAT = 4 18 | LITERAL_TYPE_BLOCK = 5 19 | LITERAL_TYPE_ASSIGNMENT = 6 20 | 21 | SEND_TYPE_UNARY = 0 22 | SEND_TYPE_BINARY = 1 23 | SEND_TYPE_KEYWORD = 2 24 | SEND_TYPE_UNARY_RESEND = 3 25 | SEND_TYPE_KEYWORD_RESEND = 4 26 | 27 | SLOT_NORMAL = 0 28 | SLOT_PARENT = 1 29 | 30 | 31 | def disassemble(bytecodes, tokens=None): 32 | disassembled = [] 33 | 34 | if not tokens: 35 | tokens = bytecode_tokenizer(bytecodes) 36 | 37 | for token in tokens: 38 | index = str(token[0]) 39 | bytecode = token[1] 40 | 41 | if bytecode == BYTECODE_SEND: 42 | send_type = token[2] 43 | number_of_params = token[3] 44 | 45 | send_type_str = { 46 | SEND_TYPE_UNARY: "UNARY", 47 | SEND_TYPE_BINARY: "BINARY", 48 | SEND_TYPE_KEYWORD: "KEYWORD", 49 | SEND_TYPE_UNARY_RESEND: "UNARY_RESEND", 50 | SEND_TYPE_KEYWORD_RESEND: "KEYWORD_RESEND", 51 | }[send_type] 52 | 53 | disassembled.append([ 54 | index, 55 | "SEND", 56 | "type:" + send_type_str, 57 | "params:" + str(number_of_params) 58 | ]) 59 | 60 | elif bytecode == BYTECODE_PUSH_SELF: 61 | disassembled.append([ 62 | index, 63 | "PUSH_SELF" 64 | ]) 65 | 66 | elif bytecode == BYTECODE_PUSH_LITERAL: 67 | literal_type = token[2] 68 | literal_index = token[3] 69 | 70 | literal_type_str = { 71 | LITERAL_TYPE_NIL: "NIL", 72 | LITERAL_TYPE_INT: "INT", 73 | LITERAL_TYPE_STR: "STR", 74 | LITERAL_TYPE_OBJ: "OBJ", 75 | LITERAL_TYPE_FLOAT: "FLOAT", 76 | LITERAL_TYPE_BLOCK: "BLOCK", 77 | LITERAL_TYPE_ASSIGNMENT: "ASSIGNMENT", 78 | }[literal_type] 79 | 80 | disassembled.append([ 81 | index, 82 | "PUSH_LITERAL", 83 | "type:" + literal_type_str, 84 | "index:" + str(literal_index) 85 | ]) 86 | 87 | elif bytecode == BYTECODE_RETURN_TOP: 88 | disassembled.append([ 89 | index, 90 | "RETURN_TOP" 91 | ]) 92 | 93 | elif bytecode == BYTECODE_RETURN_IMPLICIT: 94 | disassembled.append([ 95 | index, 96 | "RETURN_IMPLICIT" 97 | ]) 98 | 99 | elif bytecode == BYTECODE_ADD_SLOT: 100 | slot_type = token[2] 101 | slot_type_str = { 102 | SLOT_NORMAL: "SLOT_NORMAL", 103 | SLOT_PARENT: "SLOT_PARENT", 104 | }[slot_type] 105 | 106 | disassembled.append([ 107 | index, 108 | "ADD_SLOT", 109 | "type:" + slot_type_str, 110 | ]) 111 | 112 | else: 113 | disassembled.append([ 114 | index, 115 | "UNKNOWN:%s token:%s" % (str(bytecode), token) 116 | ]) 117 | 118 | return disassembled 119 | 120 | 121 | def bytecode_tokenizer(bytecodes): 122 | bytecodes = [ord(c) for c in bytecodes] 123 | bytecodes_len = len(bytecodes) 124 | while bytecodes: 125 | index = _compute_index(bytecodes_len, bytecodes) 126 | bytecode = bytecodes.pop(0) 127 | 128 | if bytecode == BYTECODE_SEND: 129 | send_type = bytecodes.pop(0) 130 | number_of_params = bytecodes.pop(0) 131 | 132 | yield [index, bytecode, send_type, number_of_params] 133 | 134 | elif bytecode == BYTECODE_PUSH_LITERAL: 135 | literal_type = bytecodes.pop(0) 136 | literal_index = bytecodes.pop(0) 137 | 138 | yield [index, bytecode, literal_type, literal_index] 139 | 140 | elif (bytecode == BYTECODE_RETURN_TOP or 141 | bytecode == BYTECODE_RETURN_IMPLICIT or 142 | bytecode == BYTECODE_PUSH_SELF): 143 | yield [index, bytecode] 144 | 145 | elif bytecode == BYTECODE_ADD_SLOT: 146 | slot_type = bytecodes.pop(0) 147 | 148 | yield [index, bytecode, slot_type] 149 | 150 | else: 151 | yield [index, bytecode] 152 | 153 | 154 | def _compute_index(bytecodes_len, bytecodes): 155 | return bytecodes_len - len(bytecodes) 156 | 157 | 158 | def bytecode_detokenizer(tokens): 159 | bytecodes = [] 160 | for token in tokens: 161 | token = token[1:] 162 | bytecodes.extend(token) 163 | 164 | return str("".join([chr(x) for x in bytecodes])) 165 | -------------------------------------------------------------------------------- /src/tinySelf/vm/code_context.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from rply.token import BaseBox 3 | 4 | from tinySelf.vm.bytecodes import disassemble 5 | from tinySelf.vm.bytecodes import LITERAL_TYPE_INT 6 | from tinySelf.vm.bytecodes import LITERAL_TYPE_STR 7 | from tinySelf.vm.bytecodes import LITERAL_TYPE_OBJ 8 | from tinySelf.vm.bytecodes import LITERAL_TYPE_FLOAT 9 | from tinySelf.vm.bytecodes import BYTECODE_PUSH_LITERAL 10 | from tinySelf.vm.bytecodes import BYTECODE_RETURN_TOP 11 | 12 | from tinySelf.vm.object_layout import Object 13 | 14 | 15 | class LiteralBox(BaseBox): 16 | def finalize(self): 17 | pass 18 | 19 | 20 | class IntBox(LiteralBox): 21 | def __init__(self, value): 22 | assert isinstance(value, int) 23 | 24 | self.value = value 25 | self.literal_type = LITERAL_TYPE_INT 26 | 27 | def __str__(self): 28 | return str(self.value) 29 | 30 | 31 | class FloatBox(LiteralBox): 32 | def __init__(self, value): 33 | assert isinstance(value, float) 34 | 35 | self.value = value 36 | self.literal_type = LITERAL_TYPE_FLOAT 37 | 38 | def __str__(self): 39 | return str(self.value) 40 | 41 | 42 | class StrBox(LiteralBox): 43 | def __init__(self, value): 44 | assert isinstance(value, str) 45 | 46 | self.value = value 47 | self.literal_type = LITERAL_TYPE_STR 48 | 49 | def __str__(self): 50 | return self.value 51 | 52 | 53 | class ObjBox(LiteralBox): 54 | def __init__(self, obj): 55 | assert isinstance(obj, Object) 56 | 57 | self.value = obj 58 | self.literal_type = LITERAL_TYPE_OBJ 59 | 60 | def finalize(self): 61 | if self.value and self.value.code_context: 62 | self.value.code_context.finalize() 63 | 64 | def __str__(self): 65 | if self.value.ast is not None: 66 | return self.value.ast.__str__() 67 | 68 | return "No obj representation" 69 | 70 | 71 | class CodeContext(object): 72 | _immutable_fields_ = ["bytecodes"] 73 | 74 | def __init__(self): 75 | self._finalized = False 76 | 77 | self.bytecodes = "" 78 | self._mutable_bytecodes = [] 79 | 80 | self.str_literal_cache = {} 81 | 82 | self.literals = [] 83 | self._params_cache = None # used to cache intermediate parameters obj 84 | 85 | # used in ._create_intermediate_params_obj() to not to agressively 86 | # cache objects with slots 87 | self.will_have_slots = False 88 | 89 | def add_literal(self, literal): 90 | assert isinstance(literal, LiteralBox) 91 | 92 | self.literals.append(literal) 93 | return len(self.literals) - 1 94 | 95 | def add_literal_str(self, literal): 96 | index = self.str_literal_cache.get(literal, -1) 97 | if index > -1: 98 | return index 99 | 100 | index = self.add_literal(StrBox(literal)) 101 | self.str_literal_cache[literal] = index 102 | 103 | return index 104 | 105 | def add_literal_int(self, literal): 106 | return self.add_literal(IntBox(literal)) 107 | 108 | def add_literal_float(self, literal): 109 | return self.add_literal(FloatBox(literal)) 110 | 111 | def add_literal_obj(self, literal): 112 | return self.add_literal(ObjBox(literal)) 113 | 114 | def add_bytecode(self, bytecode): 115 | self._mutable_bytecodes.append(bytecode) 116 | 117 | def add_literal_str_push_bytecode(self, literal): 118 | assert isinstance(literal, str) 119 | 120 | index = self.add_literal_str(literal) 121 | 122 | self.add_bytecode(BYTECODE_PUSH_LITERAL) 123 | self.add_bytecode(LITERAL_TYPE_STR) 124 | self.add_bytecode(index) 125 | 126 | return index 127 | 128 | def finalize(self): 129 | if self._finalized: 130 | return self 131 | 132 | if self._mutable_bytecodes: 133 | # 4x as 3 is maximum length of multi-bytecode instructions 134 | self._mutable_bytecodes.append(BYTECODE_RETURN_TOP) 135 | self._mutable_bytecodes.append(BYTECODE_RETURN_TOP) 136 | self._mutable_bytecodes.append(BYTECODE_RETURN_TOP) 137 | self._mutable_bytecodes.append(BYTECODE_RETURN_TOP) 138 | 139 | # I would use bytearray(), but it behaves differently under rpython 140 | self.bytecodes = str("".join([chr(x) for x in self._mutable_bytecodes])) 141 | self._mutable_bytecodes = None 142 | self.str_literal_cache = None 143 | 144 | for item in self.literals: 145 | item.finalize() 146 | 147 | self._finalized = True 148 | 149 | return self 150 | 151 | def debug_repr(self): 152 | out = '(|\n literals = (| l <- dict clone. |\n l\n' 153 | for cnt, literal in enumerate(self.literals): 154 | out += ' at: %d Put: "%s(%s)";\n' % (cnt, literal.__class__.__name__, 155 | literal.__str__()) 156 | 157 | # if isinstance(literal, ObjBox): 158 | # item = literal.value 159 | # 160 | # if item.has_code and item.code_context: 161 | # out = item.code_context.debug_repr() + "\n\n" + out 162 | 163 | # meh, rpython and his proven non-negative bounds.. 164 | index = len(out) - 2 165 | assert index >= 0 166 | out = out[:index] + '.\n ).\n\n' 167 | 168 | out += ' disassembled = (||\n' 169 | instructions = [] 170 | for instruction in disassemble(self.bytecodes): 171 | instruction_as_obj = ", ".join(['"%s"' % str(x) for x in instruction]) 172 | instruction_as_obj = str(instruction_as_obj).replace("'", '"') 173 | instructions.append(' (%s)' % instruction_as_obj) 174 | out += ", \n".join(instructions) 175 | out += '\n ).\n\n' 176 | 177 | bytecodes_list = ", ".join([str(ord(x)) for x in self.bytecodes]) 178 | out += ' bytecodes = (||\n %s\n ).\n|)' % bytecodes_list 179 | 180 | return out 181 | 182 | @property 183 | def method_stack_size(self): 184 | return len(self.literals) + 1 185 | -------------------------------------------------------------------------------- /src/tinySelf/vm/debug/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /src/tinySelf/vm/debug/plantuml_composer.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | 4 | def indent(content): 5 | return "\n".join( 6 | " " + line if line else line 7 | for line in content.splitlines() 8 | ) 9 | 10 | 11 | class Root(object): 12 | def __init__(self, items=None): 13 | self.items = items if items else [] 14 | 15 | def add(self, item): 16 | self.items.append(item) 17 | 18 | def pick_all_connections(self): 19 | pass 20 | 21 | def to_str(self): 22 | items = "\n".join( 23 | item.to_str() for item in self.items 24 | ) 25 | 26 | connections = [] 27 | for item in self.items: 28 | connections.extend(item.get_connections()) 29 | 30 | content = items + "\n".join(connections) 31 | return "@startuml\n%s\n@enduml" % content 32 | 33 | 34 | class Base(object): 35 | def raw_connect(self, raw_connect): 36 | self.connections.append(raw_connect) 37 | 38 | def connect(self, item, pos=None, type="-->", desc=""): 39 | item_name = item.name 40 | 41 | if pos: 42 | assert pos in "dulr" 43 | type = type[:1] + pos + type[1:] 44 | 45 | if not desc: 46 | self.raw_connect("%s %s %s" % (self.name, type, item_name)) 47 | else: 48 | self.raw_connect("%s %s %s: %s" % (self.name, type, item_name, desc)) 49 | 50 | 51 | class Class(Base): 52 | def __init__(self, name, alt_name=None, prop=None, methods=None): 53 | self.name = name 54 | self.alt_name = alt_name 55 | 56 | self._class_type = "class" 57 | 58 | self.properties = prop if prop else [] 59 | self.methods = methods if methods else [] 60 | self.connections = [] 61 | 62 | def add_property(self, raw_property): 63 | self.properties.append(raw_property.strip()) 64 | 65 | def add_method(self, raw_method): 66 | self.methods.append(raw_method) 67 | 68 | def get_connections(self): 69 | return self.connections 70 | 71 | def _get_alt_name(self): 72 | return '%s %s as "%s" {\n' % (self._class_type, self.name, self.alt_name) 73 | 74 | def to_str(self): 75 | if self.alt_name: 76 | o = self._get_alt_name() # because object has this backwards 77 | else: 78 | o = '%s %s {\n' % (self._class_type, self.name) 79 | 80 | if self.properties or self.methods: 81 | for item in self.properties: 82 | o += indent(item) 83 | o += "\n" 84 | 85 | if self.methods: 86 | o += indent("--") + "\n" 87 | 88 | for item in self.methods: 89 | o += indent(item) 90 | o += "\n" 91 | 92 | o += '}\n\n' 93 | 94 | return o 95 | 96 | 97 | class Abstract(Class): 98 | def __init__(self, name, alt_name=None, prop=None, methods=None): 99 | super(Abstract, self).__init__(name, alt_name=alt_name, prop=prop, methods=methods) 100 | self._class_type = "abstract" 101 | 102 | 103 | class Interface(Class): 104 | def __init__(self, name, alt_name=None, prop=None, methods=None): 105 | super(Interface, self).__init__(name, alt_name=alt_name, prop=prop, methods=methods) 106 | self._class_type = "interface" 107 | 108 | 109 | class Annotation(Class): 110 | def __init__(self, name, alt_name=None, prop=None, methods=None): 111 | super(Annotation, self).__init__(name, alt_name=alt_name, prop=prop, methods=methods) 112 | self._class_type = "annotation" 113 | 114 | 115 | class Enum(Class): 116 | def __init__(self, name, alt_name=None, prop=None, methods=None): 117 | super(Enum, self).__init__(name, alt_name=alt_name, prop=prop, methods=methods) 118 | self._class_type = "enum" 119 | 120 | 121 | class Object(Class): 122 | def __init__(self, name, alt_name=None, prop=None, methods=None): 123 | super(Object, self).__init__(name, alt_name=alt_name, prop=prop, methods=methods) 124 | self._class_type = "object" 125 | 126 | def _get_alt_name(self): 127 | return '%s "%s" as %s {\n' % (self._class_type, self.alt_name, self.name) 128 | 129 | 130 | class Package(Base): 131 | def __init__(self, name, items=None): 132 | self.name = name 133 | self._package_type = "package" 134 | 135 | self.items = items if items else [] 136 | self.connections = [] 137 | 138 | def add(self, item): 139 | self.items.append(item) 140 | 141 | def get_connections(self): 142 | connections = self.connections[:] 143 | for item in self.items: 144 | connections.extend(item.get_connections()) 145 | 146 | return connections 147 | 148 | def to_str(self): 149 | o = '%s %s {\n' % (self._package_type, self.name) 150 | 151 | for item in self.items: 152 | o += indent(item.to_str()) 153 | 154 | o += "}\n\n" 155 | 156 | return o 157 | 158 | 159 | class Node(Package): 160 | def __init__(self, name, items=None): 161 | super(Node, self).__init__(name, items) 162 | self._package_type = "node" 163 | 164 | 165 | class Folder(Package): 166 | def __init__(self, name, items=None): 167 | super(Folder, self).__init__(name, items) 168 | self._package_type = "folder" 169 | 170 | 171 | class Frame(Package): 172 | def __init__(self, name, items=None): 173 | super(Frame, self).__init__(name, items) 174 | self._package_type = "frame" 175 | 176 | 177 | class Cloud(Package): 178 | def __init__(self, name, items=None): 179 | super(Cloud, self).__init__(name, items) 180 | self._package_type = "cloud" 181 | 182 | 183 | class Database(Package): 184 | def __init__(self, name, items=None): 185 | super(Database, self).__init__(name, items) 186 | self._package_type = "database" 187 | 188 | 189 | if __name__ == '__main__': 190 | c = Class("xe", prop=["prop"], methods=["+ .method()"]) 191 | d = Object("xex") 192 | c.connect(d) 193 | 194 | p = Frame("test", [c, d]) 195 | 196 | root = Root([p]) 197 | print(root.to_str()) 198 | -------------------------------------------------------------------------------- /src/tinySelf/vm/debug/pretty_print_ast.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | def pretty_print_ast(source): 3 | """ 4 | I am really sorry for bringing yet another hacked-together state machine 5 | parser on the world, but I really needed it and oh god, sorry. 6 | 7 | If this wasn't written in the rpython, I could have tried to use something 8 | else. 9 | """ 10 | output = "" 11 | indentation = 0 12 | 13 | dont_close = False 14 | wrap_after_dash = False 15 | closing_char = {"(": ")", "[": "]", "{": "}"} 16 | for i, char in enumerate(source): 17 | next_char = source[i + 1] if i < len(source) - 1 else "" 18 | 19 | if char == ")" or char == "}" or char == "]": 20 | if not dont_close: 21 | output += "\n" 22 | indentation -= 1 23 | output += " " * indentation 24 | 25 | dont_close = False 26 | 27 | # case of ), on the end of the line 28 | if next_char == ",": 29 | wrap_after_dash = True 30 | 31 | output += char 32 | 33 | if char == "," and wrap_after_dash: 34 | wrap_after_dash = False 35 | output += "\n" 36 | output += " " * indentation 37 | output = output[:-1] 38 | 39 | elif char == "(" or char == "{" or char == "[": 40 | closed_at = source[i:].find(closing_char[char]) 41 | structure_str = source[i + 1 : i + closed_at] 42 | 43 | if next_char == closing_char[char]: 44 | dont_close = True 45 | 46 | # put thigs like Message(x) to one line, instead of three 47 | elif structure_str and not _contains_substructures(structure_str): 48 | dont_close = True 49 | else: 50 | output += "\n" 51 | indentation += 1 52 | output += " " * indentation 53 | 54 | return output 55 | 56 | 57 | def _contains_substructures(str): 58 | substructure_chars = "({[]}),\n" 59 | 60 | for char in substructure_chars: 61 | if char in str: 62 | return True 63 | 64 | return False 65 | -------------------------------------------------------------------------------- /src/tinySelf/vm/debug/visualisations.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from plantuml_composer import Root 3 | from plantuml_composer import Object as PUObject 4 | from plantuml_composer import Frame 5 | 6 | from tinySelf.vm import Object 7 | 8 | 9 | OBJ_MAP_COUNTER = 0 10 | FRAMESET_COUNTER = 0 11 | 12 | 13 | class ObjToPlantUML(object): 14 | def __init__(self, obj, recursive=False, _processed_ids=None): 15 | self.obj = obj 16 | self.recursive = recursive 17 | 18 | self._plantuml_repr = "" 19 | 20 | if _processed_ids is None: 21 | self._processed_ids = set() 22 | else: 23 | self._processed_ids = _processed_ids 24 | 25 | def _write(self, s): 26 | self._plantuml_repr += s 27 | 28 | def _writeln(self, s): 29 | self._write(s) 30 | self._write('\n') 31 | 32 | def _reset(self): 33 | self._plantuml_repr = "" 34 | self._processed_ids = set() 35 | 36 | def dump_to_file(self, file_handle): 37 | self._reset() 38 | 39 | file_handle.write("@startuml\n") 40 | 41 | plantuml_repr, connections = self._dump_obj_to_plantuml() 42 | file_handle.write(plantuml_repr) 43 | 44 | # conneections have to be written after the classes in order to use 45 | # customized names in plantuml 46 | for connection in connections: 47 | file_handle.write(connection) 48 | 49 | file_handle.write("@enduml\n") 50 | 51 | def _dump_obj_to_plantuml(self): 52 | """ 53 | Dump information about object to plantuml. 54 | 55 | Returns: 56 | tuple[str, list]: Description of all objects, list of all relations. 57 | """ 58 | if self.obj.id in self._processed_ids: 59 | return '', [] 60 | else: 61 | self._processed_ids.add(self.obj.id) 62 | 63 | name = self.obj.__class__.__name__ 64 | self._writeln('class %s as "%s (id: %s)" {' % (self.obj.id, name, self.obj.id)) 65 | 66 | self._dump_parameters() 67 | objects_to_process = [] 68 | objects_to_process.extend(self._dump_parents()) 69 | objects_to_process.extend(self._dump_slots()) 70 | self._dump_ast() 71 | 72 | self._writeln('}') 73 | 74 | relations = [] 75 | for is_parent, obj_name, other_obj in objects_to_process: 76 | obj_dumper = ObjToPlantUML(other_obj, self.recursive, self._processed_ids) 77 | obj_repr, obj_links = obj_dumper._dump_obj_to_plantuml() 78 | 79 | self._writeln(obj_repr) 80 | obj_type = "parent" if is_parent else "slot" 81 | line_type = "-->" if is_parent else "..>" 82 | 83 | relations.append('%s %s %s: %s %s\n' % (self.obj.id, line_type, other_obj.id, obj_type, obj_name)) 84 | relations.extend(obj_links) 85 | 86 | if self.obj.scope_parent: 87 | relations.append('%s --> %s: scope_parent\n' % (self.obj.id, self.obj.scope_parent.id)) 88 | 89 | obj_dumper = ObjToPlantUML(self.obj.scope_parent, self.recursive, self._processed_ids) 90 | scope_parent_repr, links = obj_dumper._dump_obj_to_plantuml() 91 | self._writeln(scope_parent_repr) 92 | relations.extend(links) 93 | 94 | return self._plantuml_repr, relations 95 | 96 | def _dump_parents(self): 97 | if self.obj._parent_slot_values is None: 98 | return 99 | 100 | self._writeln(' -- parents: --') 101 | 102 | if self.obj.scope_parent: 103 | self._writeln(' scope_parent = id(%s)' % self.obj.scope_parent.id) 104 | 105 | for parent_name, parent in zip(self.obj.map._parent_slots.keys(), self.obj._parent_slot_values): 106 | self._writeln(' ""%s*"" = id(%s)' % (parent_name, parent.id)) 107 | yield [True, parent_name, parent] 108 | 109 | def _dump_slots(self): 110 | if self.obj._slot_values is None: 111 | return 112 | 113 | self._writeln(' -- slots: --') 114 | for slot_name, slot in zip(self.obj.slot_keys, self.obj._slot_values): 115 | self._writeln(' ""%s"" = id(%s)' % (slot_name, slot.id)) 116 | 117 | if self.recursive: 118 | yield [False, slot_name, slot] 119 | 120 | def _dump_ast(self): 121 | if self.obj.ast is None or not self.obj.ast.source_pos.source_snippet: 122 | return 123 | 124 | self._writeln(' -- source: --') 125 | 126 | # creole pre-formatted text line by line; ugly, but only way I was able to make work 127 | for line in self.obj.ast.source_pos.source_snippet.splitlines(): 128 | self._writeln(' ""%s""' % line) 129 | 130 | def _dump_parameters(self): 131 | if not self.obj.parameters: 132 | return 133 | 134 | self._writeln(' -- parameters: --') 135 | for parameter in self.obj.parameters: 136 | self._writeln(' ' + parameter) # TODO: plantuml escaping 137 | 138 | 139 | def obj_map_to_plantuml(obj, number_files=False, recursive=False, prefix="obj_parent_map"): 140 | if number_files: 141 | global OBJ_MAP_COUNTER 142 | filename = "%s_%d.plantuml" % (prefix, OBJ_MAP_COUNTER) 143 | OBJ_MAP_COUNTER += 1 144 | else: 145 | filename = "%s.plantuml" % prefix 146 | 147 | obj_dumper = ObjToPlantUML(obj, recursive) 148 | with open(filename, "w") as f: 149 | obj_dumper.dump_to_file(f) 150 | 151 | 152 | def _render_object(obj, name=None): 153 | if obj is None: 154 | return PUObject("None") 155 | 156 | if not name: 157 | name = str(id(obj)) 158 | 159 | o = PUObject(name) 160 | o.add_property("__class__: " + obj.__class__.__name__) 161 | o.add_property("scope_parent: %s" % id(obj.scope_parent)) 162 | 163 | if obj.is_block: 164 | o.add_property("Block") 165 | 166 | if obj.map._parent_slots: 167 | o.add_property("---") 168 | o.add_property("Parents:") 169 | for key, val in obj.map._parent_slots.iteritems(): 170 | o.add_property("%s: %s" % (key, id(val))) 171 | 172 | if obj.slot_keys: 173 | o.add_property("---") 174 | o.add_property("Slots:") 175 | for slot_name in obj.slot_keys: 176 | o.add_property("%s: %s" % (slot_name, id(obj.get_slot(slot_name)))) 177 | 178 | return o 179 | 180 | 181 | def _render_method_stack(cnt, method_stack): 182 | f = Frame(name=str(cnt)) 183 | 184 | settings = PUObject("settings%s" % cnt, "settings", prop=[ 185 | "bc_index = %d" % method_stack.bc_index, 186 | "code_context = %s" % method_stack.code_context, 187 | # "code_context.self = %s" % method_stack.code_context.self, 188 | "error_handler = %s" % method_stack.error_handler, 189 | ]) 190 | 191 | self_obj = _render_object(method_stack.self) 192 | f.add(self_obj) 193 | settings.connect(self_obj, pos="r", desc=".code_context.self") 194 | 195 | prev = settings 196 | for i, obj in enumerate(method_stack): 197 | plantuml_obj = _render_object(obj, name="%s_%s_%s" % (id(obj), cnt, i)) 198 | 199 | f.add(plantuml_obj) 200 | if prev: 201 | prev.connect(plantuml_obj) 202 | 203 | prev = plantuml_obj 204 | 205 | f.add(settings) 206 | 207 | return f 208 | 209 | 210 | def process_stack_to_plantuml(process, numbered=False, prefix="frame"): 211 | ps = Frame(name="process_stack") 212 | 213 | settings = PUObject("settings") 214 | settings.add_property("result: %s" % process.result) 215 | settings.add_property("finished: %s" % process.finished) 216 | settings.add_property("finished_with_error: %s" % process.finished_with_error) 217 | ps.add(settings) 218 | 219 | def iterate_frames(process): 220 | frame = process.frame 221 | 222 | while frame: 223 | yield frame 224 | frame = frame.prev_stack 225 | 226 | prev = None 227 | for cnt, frame in enumerate(reversed(list(iterate_frames(process)))): 228 | plantuml_frame = _render_method_stack(cnt, frame) 229 | ps.add(plantuml_frame) 230 | 231 | if prev: 232 | prev.connect(plantuml_frame) 233 | 234 | prev = plantuml_frame 235 | 236 | obj_map_to_plantuml(process.frame.self) 237 | 238 | if numbered: 239 | global FRAMESET_COUNTER 240 | fn = "%s_%d.plantuml" % (prefix, FRAMESET_COUNTER) 241 | FRAMESET_COUNTER += 1 242 | else: 243 | fn = "%s.plantuml" % prefix 244 | 245 | with open(fn, "w") as f: 246 | f.write(Root([ps]).to_str()) 247 | 248 | 249 | if __name__ == '__main__': 250 | from tinySelf.vm.frames import MethodStack 251 | from tinySelf.vm.frames import ProcessStack 252 | 253 | p = ProcessStack() 254 | process_stack_to_plantuml(p) 255 | -------------------------------------------------------------------------------- /src/tinySelf/vm/frames.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.config import USE_LINKED_LIST_METHOD_STACK 3 | 4 | from tinySelf.vm.primitives import PrimitiveNilObject 5 | from tinySelf.vm.code_context import CodeContext 6 | from tinySelf.vm.object_layout import Object 7 | 8 | 9 | NIL = PrimitiveNilObject() 10 | 11 | 12 | class ObjectHolder(object): 13 | def __init__(self, obj, prev=None): 14 | self.obj = obj 15 | self.prev = prev 16 | 17 | 18 | class MethodStackLinkedList(object): 19 | def __init__(self, code_context=None, prev_stack=None): 20 | self._stack = None 21 | self.length = 0 22 | self.prev_stack = prev_stack 23 | 24 | self.bc_index = 0 25 | self.code_context = code_context 26 | self.error_handler = None 27 | self.self = None 28 | self.source_path = "" 29 | 30 | def push(self, obj): 31 | assert isinstance(obj, Object) 32 | if self.length == 0: 33 | self._stack = ObjectHolder(obj) 34 | self.length = 1 35 | return 36 | 37 | self._stack = ObjectHolder(obj, prev=self._stack) 38 | self.length += 1 39 | 40 | def pop(self): 41 | if self.length == 0: 42 | raise IndexError() 43 | 44 | if self.length == 1: 45 | ret = self._stack 46 | self.length = 0 47 | self._stack = None 48 | return ret.obj 49 | 50 | ret = self._stack 51 | self.length -= 1 52 | self._stack = self._stack.prev 53 | return ret.obj 54 | 55 | def pop_or_nil(self): 56 | if self.length == 0: 57 | return NIL 58 | 59 | return self.pop() 60 | 61 | def __iter__(self): 62 | out = [] 63 | item = self._stack 64 | while item is not None: 65 | out.insert(0, item.obj) 66 | item = item.prev 67 | 68 | return iter(out) 69 | 70 | 71 | class MethodStackPreallocatedArray(object): 72 | def __init__(self, code_context, prev_stack=None): 73 | self.code_context = code_context 74 | 75 | self._stack_max_size = code_context.method_stack_size 76 | self._stack = [None for _ in xrange(self._stack_max_size)] 77 | self.length = 0 78 | 79 | self.prev_stack = prev_stack 80 | 81 | self.bc_index = 0 82 | self.error_handler = None 83 | self.self = None 84 | self.source_path = "" 85 | 86 | def push(self, obj): 87 | self._stack[self.length] = obj 88 | self.length += 1 89 | 90 | def pop(self): 91 | if self.length == 0: 92 | raise IndexError() 93 | 94 | self.length -= 1 95 | ret = self._stack[self.length] 96 | self._stack[self.length] = None 97 | 98 | return ret 99 | 100 | def pop_or_nil(self): 101 | if self.length == 0: 102 | return NIL 103 | 104 | return self.pop() 105 | 106 | def __iter__(self): 107 | return iter([x for x in self._stack if x is not None]) 108 | 109 | 110 | if USE_LINKED_LIST_METHOD_STACK: 111 | MethodStack = MethodStackLinkedList 112 | else: 113 | MethodStack = MethodStackPreallocatedArray 114 | 115 | 116 | class ProcessStack(object): 117 | def __init__(self, code_context=None, source_path=""): 118 | self.frame = MethodStack(code_context) 119 | self.frame.source_path = source_path 120 | self.length = 1 121 | 122 | self.result = None 123 | self.finished = False 124 | self.finished_with_error = False 125 | 126 | def is_nested_call(self): 127 | return self.length > 1 128 | 129 | def push_frame(self, code_context, method_obj): 130 | self.frame = MethodStack(code_context, prev_stack=self.frame) 131 | self.frame.self = method_obj 132 | 133 | self.length += 1 134 | 135 | def top_frame(self): 136 | return self.frame 137 | 138 | def _cleanup_frame(self): 139 | if self.frame.self: 140 | self.frame.self.scope_parent = None 141 | 142 | def pop_frame(self): 143 | if self.length == 1: 144 | return 145 | 146 | self.frame = self.frame.prev_stack 147 | self.length -= 1 148 | 149 | def pop_and_clean_frame(self): 150 | self._cleanup_frame() 151 | self.pop_frame() 152 | 153 | def pop_frame_down(self): 154 | if self.length == 1: 155 | return 156 | 157 | result = self.frame.pop_or_nil() 158 | 159 | self.frame = self.frame.prev_stack 160 | self.length -= 1 161 | 162 | self.frame.push(result) 163 | 164 | def pop_down_and_cleanup_frame(self, raise_err=False): 165 | if raise_err and self.length == 1: 166 | raise ValueError("Nothing to pop") 167 | 168 | self._cleanup_frame() 169 | self.pop_frame_down() 170 | 171 | def first_nonblock_self(self): 172 | self_obj = self.frame.self 173 | 174 | if self_obj.is_block: 175 | return self_obj.surrounding_object 176 | else: 177 | return self_obj 178 | 179 | def __iter__(self): 180 | out = [] 181 | item = self.frame 182 | while item is not None: 183 | out.insert(0, item) 184 | item = item.prev_stack 185 | 186 | return iter(out) 187 | 188 | 189 | class ProcessCycler: 190 | def __init__(self, code_context=None): 191 | self.stash = [] 192 | 193 | self.cycler = 0 194 | self.process = None 195 | self.process_count = 0 196 | self.processes = [] 197 | 198 | if code_context is not None: 199 | self.add_process(code_context) 200 | 201 | def add_process(self, code_context, path=""): 202 | assert isinstance(code_context, CodeContext) 203 | 204 | code_context.finalize() 205 | 206 | new_process = ProcessStack(code_context, path) 207 | self.processes.append(new_process) 208 | self.process_count += 1 209 | 210 | if not self.process or self.process_count <= 0: 211 | self.process = new_process 212 | 213 | return new_process 214 | 215 | def restore_process(self, process_stack): 216 | assert isinstance(process_stack, ProcessStack) 217 | 218 | self.processes.append(process_stack) 219 | self.process_count += 1 220 | 221 | if not self.process: 222 | self.process = process_stack 223 | 224 | return process_stack 225 | 226 | def has_processes_to_run(self): 227 | return self.process_count != 0 228 | 229 | def remove_process(self, process): 230 | self.processes.remove(process) 231 | 232 | self.cycler = 0 233 | self.process_count -= 1 234 | 235 | if self.processes: 236 | self.process = self.processes[0] 237 | else: 238 | self.process = None 239 | 240 | return process 241 | 242 | def remove_active_process(self): 243 | if self.process is None: 244 | return None 245 | 246 | if self.process not in self.processes: 247 | process = self.process 248 | 249 | if self.processes: 250 | self.process = self.processes[0] 251 | 252 | return process 253 | 254 | return self.remove_process(self.process) 255 | 256 | def next_process(self): 257 | if self.process_count == 0 or self.process_count == 1: 258 | return 259 | 260 | self.process = self.processes[self.cycler] 261 | 262 | self.cycler += 1 263 | 264 | if self.cycler >= self.process_count: 265 | self.cycler = 0 266 | 267 | def stash_push(self): 268 | """ 269 | Stash all processes and context and run just current code. 270 | 271 | Used for eval() implementation, that is for immediate evaluation. 272 | """ 273 | copy = ProcessCycler() 274 | copy.cycler = self.cycler 275 | copy.process = self.process 276 | copy.process_count = self.process_count 277 | copy.processes = self.processes 278 | 279 | self.stash.append(copy) 280 | 281 | self.cycler = 0 282 | self.process = None 283 | self.process_count = 0 284 | self.processes = [] 285 | 286 | def stash_pop(self): 287 | """ 288 | Unstash processes and whole context. 289 | 290 | Used for eval() implementation, that is for immediate evaluation. 291 | """ 292 | copy = self.stash.pop() 293 | 294 | self.cycler = copy.cycler 295 | self.process = copy.process 296 | self.process_count = copy.process_count 297 | self.processes = copy.processes 298 | -------------------------------------------------------------------------------- /src/tinySelf/vm/primitives/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.vm.object_layout import Object 3 | 4 | from tinySelf.vm.primitives.mirror import Mirror 5 | 6 | from tinySelf.vm.primitives.primitive_int import INT_TRAIT 7 | from tinySelf.vm.primitives.primitive_int import PrimitiveIntObject 8 | 9 | from tinySelf.vm.primitives.primitive_float import FLOAT_TRAIT 10 | from tinySelf.vm.primitives.primitive_float import PrimitiveFloatObject 11 | 12 | from tinySelf.vm.primitives.primitive_str import STR_TRAIT 13 | from tinySelf.vm.primitives.primitive_str import PrimitiveStrObject 14 | 15 | from tinySelf.vm.primitives.primitive_nil import PrimitiveNilObject 16 | from tinySelf.vm.primitives.primitive_true import PrimitiveTrueObject 17 | from tinySelf.vm.primitives.primitive_false import PrimitiveFalseObject 18 | from tinySelf.vm.primitives.primitive_list import PrimitiveListObject 19 | from tinySelf.vm.primitives.primitive_dict import ObjectDict 20 | from tinySelf.vm.primitives.primitive_dict import PrimitiveDictObject 21 | 22 | from tinySelf.vm.primitives.add_primitive_fn import add_primitive_fn 23 | 24 | from tinySelf.vm.primitives.interpreter_primitives import ErrorObject 25 | from tinySelf.vm.primitives.interpreter_primitives import gen_interpreter_primitives 26 | 27 | from tinySelf.vm.primitives.os import get_primitive_os 28 | 29 | from tinySelf.vm.primitives.primitive_time import get_primitive_time_object 30 | 31 | from tinySelf.vm.primitives.block_traits import add_block_trait 32 | from tinySelf.vm.primitives.block_traits import _USER_EDITABLE_BLOCK_TRAIT 33 | 34 | 35 | class AssignmentPrimitive(Object): 36 | def __init__(self, real_parent=None): 37 | Object.__init__(self) 38 | self.real_parent = real_parent 39 | 40 | @property 41 | def is_assignment_primitive(self): 42 | return True 43 | 44 | @property 45 | def has_code(self): 46 | return False 47 | 48 | @property 49 | def has_primitive_code(self): 50 | return False 51 | 52 | def __str__(self): 53 | return "AssignmentPrimitive()" 54 | 55 | 56 | def _create_mirror(interpreter, self, parameters): 57 | obj = parameters[0] 58 | assert isinstance(obj, Object) 59 | 60 | return Mirror(obj) 61 | 62 | 63 | def get_primitives(): 64 | """ 65 | Return object with primitive functions mapped to its slots. 66 | 67 | Returns: 68 | obj: Instance of tinySelf's Object. 69 | """ 70 | primitives = Object() 71 | 72 | primitives.meta_add_slot("nil", PrimitiveNilObject()) 73 | primitives.meta_add_slot("true", PrimitiveTrueObject()) 74 | primitives.meta_add_slot("false", PrimitiveFalseObject()) 75 | primitives.meta_add_slot("list", PrimitiveListObject([])) 76 | primitives.meta_add_slot("dict", PrimitiveDictObject(ObjectDict.copy())) 77 | 78 | traits = Object() 79 | primitives.meta_add_slot("traits", traits) 80 | traits.meta_add_slot("int", INT_TRAIT) 81 | traits.meta_add_slot("float", FLOAT_TRAIT) 82 | traits.meta_add_slot("str", STR_TRAIT) 83 | traits.meta_add_slot("block", _USER_EDITABLE_BLOCK_TRAIT) 84 | 85 | primitives.meta_add_slot("os", get_primitive_os()) 86 | primitives.meta_add_slot("time", get_primitive_time_object()) 87 | 88 | add_primitive_fn(primitives, "mirrorOn:", _create_mirror, ["obj"]) 89 | 90 | return primitives 91 | -------------------------------------------------------------------------------- /src/tinySelf/vm/primitives/add_primitive_fn.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.parser import lex_and_parse 3 | from tinySelf.vm.object_layout import Object 4 | 5 | 6 | class PrimitiveCodeMethodObject(Object): 7 | def __str__(self): 8 | return "PrimitiveCodeMethodObject(%s)" % ", ".join(self.map._slots.keys()) 9 | 10 | 11 | def build_primitive_code_obj(primitive_fn, arguments): 12 | code_obj = PrimitiveCodeMethodObject() 13 | 14 | code_obj.map.parameters = arguments 15 | code_obj.map.primitive_code = primitive_fn 16 | 17 | return code_obj 18 | 19 | 20 | def add_primitive_fn(obj, slot_name, primitive_fn, arguments): 21 | """ 22 | Into the `obj` insert at `slot_name` `primitive_fn` expecting `arguments`. 23 | 24 | `primitive_fn` is expected to be rpython function with exactly three 25 | arguments: 26 | 27 | - interpreter (interpreter instance) 28 | - receiver (object into which is this fn bound) 29 | - parameters (list of parameters) 30 | 31 | Args: 32 | obj (Object): Instance of Object. 33 | slot_name (str): Name of the slot where the `primitive_fn` will be bound. 34 | primitive_fn (fn): RPython function which will be called. 35 | arguments (list): List of strings with named parameters. 36 | 37 | Returns: 38 | Object: Code object with bound `primitive_fn`. 39 | """ 40 | assert isinstance(obj, Object) 41 | assert isinstance(slot_name, str) 42 | assert isinstance(arguments, list) 43 | 44 | primitive_code_obj = build_primitive_code_obj(primitive_fn, arguments) 45 | obj.meta_add_slot(slot_name, primitive_code_obj) 46 | 47 | return primitive_code_obj 48 | 49 | -------------------------------------------------------------------------------- /src/tinySelf/vm/primitives/block_traits.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.vm.object_layout import Object 3 | from tinySelf.vm.primitives import PrimitiveIntObject 4 | from tinySelf.vm.primitives import PrimitiveStrObject 5 | from tinySelf.vm.primitives import add_primitive_fn 6 | 7 | 8 | class BlockTraitSingleton(Object): 9 | pass 10 | 11 | 12 | _USER_EDITABLE_BLOCK_TRAIT = BlockTraitSingleton() 13 | 14 | 15 | def BlockTrait(): 16 | return _USER_EDITABLE_BLOCK_TRAIT 17 | 18 | 19 | def _print_block_source(interpreter, block_obj, parameters): 20 | ast = block_obj.get_slot("value").ast 21 | 22 | if ast is not None: 23 | return PrimitiveStrObject(ast.source_pos.source_snippet) 24 | 25 | return PrimitiveStrObject(block_obj.__str__()) 26 | 27 | 28 | def _get_lineno(interpreter, block_obj, parameters): 29 | ast = block_obj.get_slot("value").ast 30 | return PrimitiveIntObject(ast.source_pos.start_line) 31 | 32 | 33 | def add_block_trait(block): 34 | obj = Object() 35 | obj.meta_add_slot("value", block) 36 | obj.meta_add_slot("with:", block) 37 | obj.meta_add_slot("with:With:", block) 38 | obj.meta_add_slot("with:With:With:", block) 39 | obj.meta_add_slot("with:With:With:With:", block) 40 | obj.meta_add_slot("withAll:", block) 41 | 42 | # obj.meta_add_parent("trait", BlockTrait()) 43 | add_primitive_fn(obj, "asString", _print_block_source, []) 44 | add_primitive_fn(obj, "getLineNumber", _get_lineno, []) 45 | 46 | obj.scope_parent = BlockTrait() 47 | 48 | return obj 49 | -------------------------------------------------------------------------------- /src/tinySelf/vm/primitives/cache.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Cache object used in some primitives (int, float, str, ..) to hold value of map 4 | and slots, so it is not created again and again. 5 | 6 | For example, it doesn't really make any sense to create new maps for ints, 7 | and it also doesn't really make any sense to .clone() them, because their 8 | .value property is immutable. 9 | """ 10 | 11 | 12 | class ObjCache(object): 13 | def __init__(self): 14 | self.map = None 15 | self.slots = None 16 | self.parents = None 17 | 18 | self.is_set = False 19 | 20 | def store(self, obj): 21 | """ 22 | Args: 23 | obj (Object): 24 | """ 25 | self.map = obj.map 26 | self.slots = obj._slot_values 27 | self.parents = obj._parent_slot_values 28 | 29 | self.is_set = True 30 | 31 | def restore(self, obj): 32 | obj.map = self.map 33 | obj._slot_values = self.slots 34 | obj._parent_slot_values = self.parents 35 | -------------------------------------------------------------------------------- /src/tinySelf/vm/primitives/interpreter_primitives.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from rpython.rlib.objectmodel import we_are_translated 3 | 4 | from tinySelf.vm.primitives import PrimitiveStrObject 5 | from tinySelf.vm.primitives import PrimitiveIntObject 6 | from tinySelf.vm.primitives import PrimitiveNilObject 7 | from tinySelf.vm.primitives.add_primitive_fn import add_primitive_fn 8 | 9 | from tinySelf.vm.object_layout import Object 10 | 11 | from tinySelf.vm.code_context import ObjBox 12 | from tinySelf.vm.code_context import CodeContext 13 | 14 | from tinySelf.parser import lex_and_parse 15 | from tinySelf.parser import lex_and_parse_as_root 16 | from tinySelf.parser.ast_tokens import Object as AstObject 17 | 18 | 19 | NIL = PrimitiveNilObject() 20 | 21 | 22 | class ErrorObject(Object): 23 | def __init__(self, message, process_stack): 24 | Object.__init__(self) 25 | self.message = message 26 | self.process_stack = process_stack 27 | 28 | @property 29 | def has_code(self): 30 | return False 31 | 32 | @property 33 | def has_primitive_code(self): 34 | return False 35 | 36 | def __str__(self): 37 | return "ErrorObject(%s)" % self.message 38 | 39 | 40 | def primitive_fn_get_number_of_processes(interpreter, self, parameters): 41 | return PrimitiveIntObject(len(interpreter.processes)) 42 | 43 | 44 | def primitive_fn_get_number_of_stack_frames(interpreter, self, parameters): 45 | return PrimitiveIntObject(interpreter.process.length) 46 | 47 | 48 | def primitive_fn_set_error_handler(interpreter, self, parameters): 49 | blck = parameters[0] 50 | assert isinstance(blck, Object) 51 | 52 | interpreter.process.frame.error_handler = blck 53 | 54 | return NIL 55 | 56 | 57 | def primitive_fn_get_frame_with_error_handler(frame_linked_list): 58 | if frame_linked_list is None: 59 | return None 60 | 61 | while frame_linked_list is not None: 62 | if frame_linked_list.error_handler is not None: 63 | return frame_linked_list 64 | 65 | frame_linked_list = frame_linked_list.prev_stack 66 | 67 | return None 68 | 69 | 70 | def primitive_fn_halt(interpreter, self, parameters): 71 | obj = parameters[0] 72 | assert isinstance(obj, Object) 73 | 74 | process = interpreter.remove_active_process() 75 | 76 | if interpreter.process_count == 0: 77 | interpreter.process = process 78 | 79 | process.result = obj 80 | process.finished = True 81 | process.finished_with_error = False 82 | 83 | return obj 84 | 85 | 86 | def primitive_fn_restore_process_with(interpreter, self, parameters): 87 | error_obj = parameters[0] 88 | assert isinstance(error_obj, Object) 89 | with_obj = parameters[1] 90 | assert isinstance(with_obj, Object) 91 | 92 | 93 | if not isinstance(error_obj, ErrorObject): 94 | raise ValueError("This is not instance of error object!") 95 | 96 | process = error_obj.process_stack 97 | process.frame.push(with_obj) 98 | interpreter.restore_process(process) 99 | 100 | return None 101 | 102 | 103 | def primitive_fn_restore_process(interpreter, self, parameters): 104 | obj = parameters[0] 105 | assert isinstance(obj, Object) 106 | 107 | if not isinstance(obj, ErrorObject): 108 | raise ValueError("This is not instance of error object!") 109 | 110 | obj.process_stack.push_frame(CodeContext(), NIL) # it is immediatelly poped anyway 111 | interpreter.restore_process(obj.process_stack) 112 | 113 | return None 114 | 115 | 116 | def primitive_fn_raise_error(interpreter, self, parameters): 117 | msg = parameters[0] 118 | assert isinstance(msg, Object) 119 | 120 | frame_with_handler = primitive_fn_get_frame_with_error_handler(interpreter.process.frame) 121 | process = interpreter.remove_active_process() 122 | 123 | if frame_with_handler is None: 124 | process.result = msg 125 | process.finished = True 126 | process.finished_with_error = True 127 | 128 | return None 129 | 130 | error_handler = frame_with_handler.error_handler.get_slot("with:With:") 131 | if error_handler is None: 132 | raise ValueError("Error handler must react to with:With: message!") 133 | 134 | error_handler.scope_parent = interpreter._create_intermediate_params_obj( 135 | error_handler.scope_parent, 136 | error_handler, 137 | [msg, ErrorObject(msg, process)] # process is passed to the error_handler 138 | ) 139 | 140 | process = interpreter.add_process(error_handler.code_context) 141 | process.frame.self = error_handler.scope_parent 142 | 143 | return None 144 | 145 | 146 | def run_after_primitive_ends(interpreter, scope_parent, method_obj, parameters=None): 147 | """ 148 | Run `method_obj` after the primitive ends. This works similarly like eval, 149 | but evaluates `method_object` instead. 150 | """ 151 | real_parameters = [] 152 | if parameters is not None: 153 | real_parameters = parameters 154 | 155 | interpreter._push_code_obj_for_interpretation( 156 | next_bytecode=0, # disable TCO 157 | scope_parent=scope_parent, 158 | method_obj=method_obj, 159 | parameters=real_parameters, 160 | ) 161 | 162 | 163 | def primitive_fn_run_script(interpreter, scope_parent, parameters): 164 | path = parameters[0] 165 | assert isinstance(path, Object) 166 | 167 | if not isinstance(path, PrimitiveStrObject): 168 | return primitive_fn_raise_error( 169 | interpreter, 170 | scope_parent, 171 | [PrimitiveStrObject("runScript: str parameter expected")] 172 | ) 173 | 174 | try: 175 | with open(path.value) as f: 176 | source = f.read() 177 | except Exception as e: 178 | return primitive_fn_raise_error( 179 | interpreter, 180 | scope_parent, 181 | [PrimitiveStrObject("runScript: %s" % str(e))] 182 | ) 183 | 184 | ast_root = lex_and_parse_as_root(source) 185 | 186 | if not ast_root.ast: 187 | return 188 | 189 | code = ast_root.compile(CodeContext()) 190 | code.finalize() 191 | 192 | method_obj = Object() 193 | method_obj.code_context = code 194 | 195 | interpreter._push_code_obj_for_interpretation( 196 | next_bytecode=0, # disable TCO 197 | scope_parent=scope_parent, 198 | method_obj=method_obj, 199 | parameters=[], 200 | ) 201 | 202 | interpreter.process.frame.source_path = path.value 203 | 204 | 205 | def call_tinyself_code_from_primitive(interpreter, code_str, code_parameters_values): 206 | """ 207 | Call `code_str` when the primitive you are currently running finishes. 208 | 209 | Use with caution, as this will be return value from your primitive. 210 | 211 | Warning: Code is pushed on stack and runs AFTER your primitive ends. Your 212 | primitive have to return None. 213 | 214 | Args: 215 | interpreter (obj): Instance of the interpreter in which context the 216 | primitive which called this function is running. 217 | code_str (str): String to be interpreted. It should be object. 218 | code_parameters_values (list): List of tinySelf's objects which will be 219 | passed as parameters. 220 | """ 221 | assert isinstance(code_str, str) 222 | 223 | wrapping_obj = lex_and_parse(code_str)[0] 224 | assert isinstance(wrapping_obj, AstObject) 225 | 226 | code = wrapping_obj.compile() 227 | code.finalize() 228 | 229 | method_obj = Object() 230 | method_obj.map.ast = wrapping_obj 231 | method_obj.scope_parent = interpreter.process.frame.self 232 | 233 | # this may be a bit unintuitive, but whole surrounding object is compiled 234 | # and I want just the code object without slot pushing, which is the first 235 | # object literal in the compiled object 236 | first_literal = code.literals[0] 237 | assert isinstance(first_literal, ObjBox) 238 | method_obj.map.code_context = first_literal.value.code_context 239 | 240 | interpreter._push_code_obj_for_interpretation( 241 | next_bytecode=0, # disable TCO 242 | scope_parent=interpreter.process.frame.self, 243 | method_obj=method_obj, 244 | parameters=code_parameters_values, 245 | ) 246 | 247 | 248 | class EvalException(Exception): 249 | def __init__(self, msg, process): 250 | if not we_are_translated(): 251 | super(EvalException, self).__init__(msg) 252 | 253 | self.message = msg 254 | self.process = process 255 | 256 | 257 | def eval_immediately(interpreter, scope_parent, self_obj, method, 258 | method_parameters, raise_exception=False): 259 | """ 260 | Run `method` in the `interpreter` immediately - that is stop everything 261 | else, disable parallelism, and run the code until it ends. 262 | 263 | Warning: 264 | This should be used only when the result is really immediately needed 265 | in the primitive. If you need standard eval, use 266 | :fn:`call_tinyself_code_from_primitive`. 267 | 268 | Args: 269 | interpreter (Interpreter): Instance of the interpreter. 270 | scope_parent (Object): This will be used as scope parent for method. 271 | self_obj (Object): Context in which `method` is defined. 272 | method (Object): Code object to run. 273 | method_parameters (list): List of parameters for `method`. 274 | raise_exception (bool): Raise exception or raise internal tinySelf 275 | error? Default `False`. 276 | 277 | Raises: 278 | EvalException: If the `method` finished with error and `raise_exception` 279 | is set to `True`. 280 | 281 | Returns: 282 | Object: Result of the running process. 283 | """ 284 | interpreter.stash_push() 285 | 286 | process = interpreter.add_process(method.code_context) 287 | 288 | # scope_parent in `self_obj` because PUSH_SELF bytecode is one of the first 289 | # instructions executed and practically everything happens in the context 290 | # of "self", not in the context of the code method 291 | self_obj.scope_parent = interpreter._create_intermediate_params_obj( 292 | scope_parent=scope_parent, 293 | method_obj=method, 294 | parameters=method_parameters, 295 | prev_scope_parent=method.scope_parent, 296 | ) 297 | interpreter.process.frame.self = self_obj 298 | 299 | interpreter.interpret() 300 | 301 | interpreter.stash_pop() 302 | 303 | if process.finished_with_error: 304 | if raise_exception: 305 | raise EvalException("Process finished with error %s" % process.result, 306 | process) 307 | else: 308 | return primitive_fn_raise_error(interpreter, method, [process.result]) 309 | 310 | return process.result 311 | 312 | 313 | def primitive_fn_eval_method_obj(interpreter, scope_parent, parameters): 314 | code = parameters[0] 315 | assert isinstance(code, PrimitiveStrObject) 316 | 317 | call_tinyself_code_from_primitive(interpreter, code.value, []) 318 | 319 | 320 | def primitive_fn_get_script_path(interpreter, scope_parent, parameters): 321 | paths = [frame.source_path 322 | for frame in interpreter.process 323 | if frame.source_path] 324 | 325 | if not paths: 326 | return PrimitiveStrObject("") 327 | 328 | return PrimitiveStrObject(paths.pop()) 329 | 330 | 331 | def gen_interpreter_primitives(interpreter): 332 | interpreter_namespace = Object() 333 | 334 | add_primitive_fn(interpreter_namespace, "numberOfProcesses", 335 | primitive_fn_get_number_of_processes, []) 336 | add_primitive_fn(interpreter_namespace, "numberOfFrames", 337 | primitive_fn_get_number_of_stack_frames, []) 338 | add_primitive_fn(interpreter_namespace, "setErrorHandler:", primitive_fn_set_error_handler, 339 | ["blck"]) 340 | add_primitive_fn(interpreter_namespace, "error:", primitive_fn_raise_error, ["obj"]) 341 | add_primitive_fn(interpreter_namespace, "halt:", primitive_fn_halt, ["obj"]) 342 | add_primitive_fn(interpreter_namespace, "restoreProcess:", primitive_fn_restore_process, 343 | ["err_obj"]) 344 | add_primitive_fn(interpreter_namespace, "restoreProcess:With:", 345 | primitive_fn_restore_process_with, ["err_obj", "with"]) 346 | add_primitive_fn(interpreter_namespace, "runScript:", primitive_fn_run_script, ["path"]) 347 | add_primitive_fn(interpreter_namespace, "evalMethodObj:", 348 | primitive_fn_eval_method_obj, ["code"]) 349 | add_primitive_fn(interpreter_namespace, "scriptPath", 350 | primitive_fn_get_script_path, []) 351 | 352 | return interpreter_namespace 353 | -------------------------------------------------------------------------------- /src/tinySelf/vm/primitives/mirror.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.vm.object_layout import Object 3 | from tinySelf.vm.primitives.primitive_str import PrimitiveStrObject 4 | from tinySelf.vm.primitives.add_primitive_fn import add_primitive_fn 5 | 6 | 7 | def primitive_add_slot(interpreter, pseudo_self, parameters): 8 | assert isinstance(pseudo_self, Mirror) 9 | name = parameters[0] 10 | assert isinstance(name, PrimitiveStrObject) 11 | val = parameters[1] 12 | assert isinstance(val, Object) 13 | 14 | pseudo_self.obj_to_mirror.meta_add_slot(name.value, val) 15 | val.scope_parent = pseudo_self.obj_to_mirror 16 | 17 | return pseudo_self.obj_to_mirror 18 | 19 | 20 | def primitive_list_slots(interpreter, pseudo_self, parameters): 21 | from tinySelf.vm.primitives.primitive_list import PrimitiveListObject 22 | assert isinstance(pseudo_self, Mirror) 23 | 24 | return PrimitiveListObject( 25 | [PrimitiveStrObject(x) for x in pseudo_self.obj_to_mirror.slot_keys] 26 | ) 27 | 28 | 29 | def primitive_add_parent(interpreter, pseudo_self, parameters): 30 | assert isinstance(pseudo_self, Mirror) 31 | name = parameters[0] 32 | assert isinstance(name, PrimitiveStrObject) 33 | val = parameters[1] 34 | assert isinstance(val, Object) 35 | 36 | pseudo_self.obj_to_mirror.meta_add_parent(name.value, val) 37 | 38 | return pseudo_self.obj_to_mirror 39 | 40 | 41 | def primitive_list_parents(interpreter, pseudo_self, parameters): 42 | from tinySelf.vm.primitives.primitive_list import PrimitiveListObject 43 | assert isinstance(pseudo_self, Mirror) 44 | 45 | return PrimitiveListObject([ 46 | PrimitiveStrObject(x) 47 | for x in pseudo_self.obj_to_mirror.parent_slot_keys 48 | ]) 49 | 50 | 51 | class Mirror(Object): 52 | def __init__(self, obj_to_mirror, obj_map=None): 53 | Object.__init__(self, obj_map) 54 | 55 | assert isinstance(obj_to_mirror, Object) 56 | self.obj_to_mirror = obj_to_mirror 57 | 58 | add_primitive_fn(self, "toSlot:Add:", primitive_add_slot, ["name", "obj"]) 59 | add_primitive_fn(self, "toParent:Add:", primitive_add_parent, ["name", "obj"]) 60 | add_primitive_fn(self, "listSlots", primitive_list_slots, []) 61 | add_primitive_fn(self, "listParents", primitive_list_parents, []) 62 | 63 | def __eq__(self, obj): 64 | if not isinstance(obj, Mirror): 65 | return False 66 | 67 | return self.obj_to_mirror is obj.obj_to_mirror 68 | 69 | def __str__(self): 70 | return "Mirror(%s)" % self.obj_to_mirror.__str__() 71 | -------------------------------------------------------------------------------- /src/tinySelf/vm/primitives/os/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.vm.object_layout import Object 3 | 4 | from tinySelf.vm.primitives.os.primitive_files import get_primitive_files 5 | from tinySelf.vm.primitives.os.primitive_socket import get_primitive_socket 6 | 7 | 8 | def get_primitive_os(): 9 | os_obj = Object() 10 | os_obj.meta_add_slot("files", get_primitive_files()) 11 | os_obj.meta_add_slot("socket", get_primitive_socket()) 12 | 13 | return os_obj 14 | -------------------------------------------------------------------------------- /src/tinySelf/vm/primitives/os/primitive_files.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os.path 3 | 4 | from rpython.rlib.rfile import RFile 5 | from rpython.rlib.rfile import create_file 6 | 7 | from tinySelf.vm.primitives import PrimitiveIntObject 8 | from tinySelf.vm.primitives import PrimitiveStrObject 9 | from tinySelf.vm.primitives import PrimitiveTrueObject 10 | from tinySelf.vm.primitives import PrimitiveFalseObject 11 | from tinySelf.vm.object_layout import Object 12 | 13 | from tinySelf.vm.primitives.cache import ObjCache 14 | from tinySelf.vm.primitives.add_primitive_fn import add_primitive_fn 15 | from tinySelf.vm.primitives.interpreter_primitives import primitive_fn_raise_error 16 | from tinySelf.vm.primitives.interpreter_primitives import run_after_primitive_ends 17 | 18 | 19 | def close_file(interpreter, pseudo_self, parameters): 20 | assert isinstance(pseudo_self, PrimitiveFileObject) 21 | 22 | pseudo_self.value.close() 23 | return pseudo_self 24 | 25 | 26 | def flush_file(interpreter, pseudo_self, parameters): 27 | assert isinstance(pseudo_self, PrimitiveFileObject) 28 | 29 | pseudo_self.value.flush() 30 | return pseudo_self 31 | 32 | 33 | def tell_file(interpreter, pseudo_self, parameters): 34 | assert isinstance(pseudo_self, PrimitiveFileObject) 35 | 36 | return PrimitiveIntObject(pseudo_self.value.tell()) 37 | 38 | 39 | def read_file(interpreter, pseudo_self, parameters): 40 | assert isinstance(pseudo_self, PrimitiveFileObject) 41 | 42 | return PrimitiveStrObject(pseudo_self.value.read()) 43 | 44 | 45 | def read_file_ammount(interpreter, pseudo_self, parameters): 46 | read_ammount_obj = parameters[0] 47 | assert isinstance(read_ammount_obj, PrimitiveIntObject) 48 | assert isinstance(pseudo_self, PrimitiveFileObject) 49 | 50 | read_ammount = int(read_ammount_obj.value) 51 | assert isinstance(read_ammount, int) 52 | 53 | return PrimitiveStrObject(pseudo_self.value.read(read_ammount)) 54 | 55 | 56 | def write_to_file(interpreter, pseudo_self, parameters): 57 | data_obj = parameters[0] 58 | assert isinstance(data_obj, PrimitiveStrObject) 59 | assert isinstance(pseudo_self, PrimitiveFileObject) 60 | 61 | pseudo_self.value.write(data_obj.value) 62 | 63 | return pseudo_self 64 | 65 | 66 | def seek_to(interpreter, pseudo_self, parameters): 67 | seek_to_obj = parameters[0] 68 | assert isinstance(seek_to_obj, PrimitiveIntObject) 69 | assert isinstance(pseudo_self, PrimitiveFileObject) 70 | 71 | seek_to = int(seek_to_obj.value) 72 | assert isinstance(seek_to, int) 73 | 74 | pseudo_self.value.seek(seek_to) 75 | 76 | return pseudo_self 77 | 78 | 79 | def is_closed(interpreter, pseudo_self, parameters): 80 | assert isinstance(pseudo_self, PrimitiveFileObject) 81 | 82 | if pseudo_self.value.closed: 83 | return PrimitiveTrueObject() 84 | 85 | return PrimitiveFalseObject() 86 | 87 | 88 | class PrimitiveFileObject(Object): 89 | _OBJ_CACHE = ObjCache() 90 | _immutable_fields_ = ["value"] 91 | def __init__(self, value, obj_map=None, path=""): 92 | Object.__init__(self, PrimitiveFileObject._OBJ_CACHE.map) 93 | 94 | # [translation:ERROR] AttributeError: 'FrozenDesc' object has no attribute 95 | # 'getuniqueclassdef' 96 | assert isinstance(value, RFile) 97 | self.value = value 98 | self.path = path 99 | 100 | if PrimitiveFileObject._OBJ_CACHE.is_set: 101 | PrimitiveFileObject._OBJ_CACHE.restore(self) 102 | return 103 | 104 | add_primitive_fn(self, "close", close_file, []) 105 | add_primitive_fn(self, "flush", flush_file, []) 106 | add_primitive_fn(self, "tell", tell_file, []) 107 | add_primitive_fn(self, "read", read_file, []) 108 | add_primitive_fn(self, "read:", read_file_ammount, ["ammount"]) 109 | add_primitive_fn(self, "write:", write_to_file, ["data"]) 110 | add_primitive_fn(self, "seek:", seek_to, ["pos"]) 111 | add_primitive_fn(self, "closed?", is_closed, []) 112 | 113 | PrimitiveFileObject._OBJ_CACHE.store(self) 114 | 115 | def __str__(self): 116 | status = ", closed" if self.value.closed else "" 117 | return "FileObject(%s%s)" % (self.path, status) 118 | 119 | def __eq__(self, obj): 120 | if not isinstance(obj, PrimitiveFileObject): 121 | return False 122 | 123 | return self.value == obj.value 124 | 125 | 126 | def open_file(interpreter, pseudo_self, parameters): 127 | path_obj = parameters[0] 128 | assert isinstance(path_obj, PrimitiveStrObject) 129 | path = path_obj.value 130 | 131 | if os.path.exists(path): 132 | return PrimitiveFileObject(create_file(path), path=path) 133 | 134 | primitive_fn_raise_error(interpreter, None, 135 | [PrimitiveStrObject("File `%s` not found." % path)]) 136 | 137 | 138 | def open_file_err(interpreter, pseudo_self, parameters): 139 | path_obj = parameters[0] 140 | assert isinstance(path_obj, PrimitiveStrObject) 141 | path = path_obj.value 142 | err_blck = parameters[1] 143 | assert isinstance(path_obj, Object) 144 | 145 | if os.path.exists(path): 146 | return PrimitiveFileObject(create_file(path), path=path) 147 | 148 | scope_parent = interpreter.process.frame.self 149 | run_after_primitive_ends(interpreter, scope_parent, err_blck.get_slot("value")) 150 | 151 | 152 | def open_file_mode_fails(interpreter, pseudo_self, parameters): 153 | path_obj = parameters[0] 154 | assert isinstance(path_obj, PrimitiveStrObject) 155 | path = path_obj.value 156 | mode = parameters[1] 157 | assert isinstance(mode, PrimitiveStrObject) 158 | err_blck = parameters[2] 159 | assert isinstance(err_blck, Object) 160 | 161 | scope_parent = interpreter.process.frame.self 162 | 163 | if len(mode.value) != 1 or mode.value[0] not in "rwaU": 164 | run_after_primitive_ends(interpreter, scope_parent, err_blck.get_slot("value"), 165 | [PrimitiveStrObject("Mode must be r/w/a/U.")]) 166 | return 167 | 168 | if mode.value == "r" and not os.path.exists(path): 169 | run_after_primitive_ends(interpreter, scope_parent, err_blck.get_slot("value"), 170 | [PrimitiveStrObject("File `%s` doesn't exists!" % path)]) 171 | return 172 | 173 | return PrimitiveFileObject(create_file(path, mode.value), path=path) 174 | 175 | 176 | def get_primitive_files(): 177 | files_obj = Object() 178 | 179 | add_primitive_fn(files_obj, "open:", open_file, ["path"]) 180 | add_primitive_fn(files_obj, "open:Fails:", open_file_err, ["path", "err"]) 181 | add_primitive_fn(files_obj, "open:Mode:Fails:", open_file_mode_fails, 182 | ["path", "mode", "err"]) 183 | 184 | return files_obj 185 | -------------------------------------------------------------------------------- /src/tinySelf/vm/primitives/os/primitive_socket.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from rpython.rlib.rsocket import AF_INET 3 | from rpython.rlib.rsocket import SOCK_STREAM 4 | from rpython.rlib.rsocket import RSocket 5 | from rpython.rlib.rsocket import INETAddress 6 | 7 | from tinySelf.vm.object_layout import Object 8 | 9 | from tinySelf.vm.primitives.cache import ObjCache 10 | from tinySelf.vm.primitives.primitive_int import PrimitiveIntObject 11 | from tinySelf.vm.primitives.primitive_int import PrimitiveStrObject 12 | from tinySelf.vm.primitives.add_primitive_fn import add_primitive_fn 13 | 14 | 15 | class PrimitiveINETAddress(Object): 16 | def __init__(self): 17 | pass 18 | 19 | 20 | def socket_recv(interpreter, pseudo_self, parameters): 21 | size_obj = parameters[0] 22 | assert isinstance(size_obj, PrimitiveIntObject) 23 | assert isinstance(pseudo_self, PrimitiveSocketObject) 24 | 25 | return PrimitiveStrObject(pseudo_self.value.recv(int(size_obj.value))) 26 | 27 | 28 | def socket_sendall(interpreter, pseudo_self, parameters): 29 | content_obj = parameters[0] 30 | assert isinstance(content_obj, PrimitiveStrObject) 31 | assert isinstance(pseudo_self, PrimitiveSocketObject) 32 | 33 | pseudo_self.value.sendall(content_obj.value) 34 | return pseudo_self 35 | 36 | 37 | def socket_close(interpreter, pseudo_self, parameters): 38 | assert isinstance(pseudo_self, PrimitiveSocketObject) 39 | 40 | pseudo_self.value.close() 41 | return pseudo_self 42 | 43 | 44 | class PrimitiveSocketObject(Object): 45 | _OBJ_CACHE = ObjCache() 46 | _immutable_fields_ = ["value"] 47 | def __init__(self, value, obj_map=None): 48 | Object.__init__(self, PrimitiveSocketObject._OBJ_CACHE.map) 49 | 50 | assert isinstance(value, RSocket) 51 | self.value = value 52 | 53 | if PrimitiveSocketObject._OBJ_CACHE.is_set: 54 | PrimitiveSocketObject._OBJ_CACHE.restore(self) 55 | return 56 | 57 | add_primitive_fn(self, "recv:", socket_recv, ["size"]) 58 | add_primitive_fn(self, "sendAll:", socket_sendall, ["data"]) 59 | add_primitive_fn(self, "close", socket_close, []) 60 | 61 | PrimitiveSocketObject._OBJ_CACHE.store(self) 62 | 63 | def __str__(self): 64 | return "PrimitiveSocket()" 65 | 66 | def __eq__(self, obj): 67 | if not isinstance(obj, PrimitiveSocketObject): 68 | return False 69 | 70 | return self.value == obj.value 71 | 72 | 73 | def open_socket(interpreter, pseudo_self, parameters): 74 | host_obj = parameters[0] 75 | assert isinstance(host_obj, PrimitiveStrObject) 76 | port_obj = parameters[1] 77 | assert isinstance(port_obj, PrimitiveIntObject) 78 | 79 | # TODO: also add for IPv6 and unix 80 | addr = INETAddress(host_obj.value, int(port_obj.value)) 81 | socket = RSocket(AF_INET, SOCK_STREAM) 82 | socket.connect(addr) 83 | 84 | return PrimitiveSocketObject(socket) 85 | 86 | 87 | def get_primitive_socket(): 88 | socket_obj = Object() 89 | 90 | add_primitive_fn(socket_obj, "open:Port:", open_socket, ["addr", "port"]) 91 | 92 | return socket_obj 93 | -------------------------------------------------------------------------------- /src/tinySelf/vm/primitives/primitive_dict.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from collections import OrderedDict 3 | 4 | from rpython.rlib.objectmodel import r_ordereddict 5 | 6 | from tinySelf.vm.object_layout import Object 7 | 8 | from tinySelf.vm.primitives.cache import ObjCache 9 | from tinySelf.vm.primitives.primitive_int import PrimitiveIntObject 10 | from tinySelf.vm.primitives.primitive_str import PrimitiveStrObject 11 | from tinySelf.vm.primitives.primitive_nil import PrimitiveNilObject 12 | from tinySelf.vm.primitives.primitive_false import PrimitiveFalseObject 13 | from tinySelf.vm.primitives.primitive_float import PrimitiveFloatObject 14 | from tinySelf.vm.primitives.add_primitive_fn import add_primitive_fn 15 | from tinySelf.vm.primitives.interpreter_primitives import eval_immediately 16 | from tinySelf.vm.primitives.interpreter_primitives import primitive_fn_raise_error 17 | 18 | 19 | class GlobalContext(object): 20 | def __init__(self): 21 | self.interpreter = None 22 | self.hash = None 23 | self.scope_parent = None 24 | 25 | 26 | GLOBAL_CONTEXT = GlobalContext() 27 | 28 | 29 | def eq_fn(obj, other): 30 | interpreter = GLOBAL_CONTEXT.interpreter 31 | 32 | eq_slot = obj.get_slot("==") 33 | if eq_slot is None: 34 | primitive_fn_raise_error(interpreter, None, 35 | [PrimitiveStrObject("== slot not found.")]) 36 | return False 37 | 38 | if eq_slot.has_primitive_code: 39 | result = eq_slot.primitive_code(interpreter, obj, [other]) 40 | else: 41 | result = eval_immediately( 42 | interpreter=interpreter, 43 | scope_parent=GLOBAL_CONTEXT.scope_parent, 44 | self_obj=obj, 45 | method=eq_slot, 46 | method_parameters=[other], 47 | raise_exception=False 48 | ) 49 | if result is None: 50 | return False 51 | 52 | if result == PrimitiveNilObject(): 53 | return False 54 | if result == PrimitiveFalseObject(): 55 | return False 56 | 57 | return True 58 | 59 | 60 | def hash_fn(obj): 61 | hash_slot = obj.get_slot("hash") 62 | 63 | # compute hash from the slot names 64 | if hash_slot is None: 65 | hash = 0 66 | for c in "".join(obj.slot_keys): 67 | hash += ord(c) 68 | 69 | return hash 70 | 71 | interpreter = GLOBAL_CONTEXT.interpreter 72 | result = eval_immediately( 73 | interpreter=interpreter, 74 | scope_parent=GLOBAL_CONTEXT.scope_parent, 75 | self_obj=obj, 76 | method=hash_slot, 77 | method_parameters=[], 78 | raise_exception=True 79 | ) 80 | 81 | if not isinstance(result, PrimitiveIntObject): 82 | msg = "Object's `hash` message must return int when put to dict." 83 | primitive_fn_raise_error(interpreter, None, [PrimitiveStrObject(msg)]) 84 | return 1 85 | 86 | return int(result.value) 87 | 88 | 89 | ObjectDict = r_ordereddict(eq_fn, hash_fn) 90 | 91 | 92 | def dict_clone(interpreter, self, parameters): 93 | assert isinstance(self, PrimitiveDictObject) 94 | 95 | return self._copy_with_primitives(PrimitiveDictObject(ObjectDict.copy())) 96 | 97 | 98 | def dict_at(interpreter, self, parameters): 99 | key = parameters[0] 100 | assert isinstance(key, Object) 101 | assert isinstance(self, PrimitiveDictObject) 102 | 103 | GLOBAL_CONTEXT.interpreter = interpreter 104 | GLOBAL_CONTEXT.scope_parent = interpreter.process.frame.self 105 | 106 | result = self.value.get(key, None) 107 | 108 | if result is None: 109 | return PrimitiveNilObject() 110 | 111 | return result 112 | 113 | 114 | def dict_length(interpreter, self, parameters): 115 | assert isinstance(self, PrimitiveDictObject) 116 | 117 | return PrimitiveIntObject(len(self.value)) 118 | 119 | 120 | def dict_at_key_put_obj(interpreter, self, parameters): 121 | key = parameters[0] 122 | obj = parameters[1] 123 | assert isinstance(self, PrimitiveDictObject) 124 | assert isinstance(key, Object) 125 | assert isinstance(obj, Object) 126 | 127 | GLOBAL_CONTEXT.interpreter = interpreter 128 | GLOBAL_CONTEXT.scope_parent = interpreter.process.frame.self 129 | 130 | self.value[key] = obj 131 | 132 | return obj 133 | 134 | 135 | class PrimitiveDictObject(Object): 136 | _OBJ_CACHE = ObjCache() 137 | _immutable_fields_ = ["value"] 138 | def __init__(self, value, obj_map=None): 139 | Object.__init__(self, PrimitiveDictObject._OBJ_CACHE.map) 140 | 141 | self.value = value 142 | 143 | if PrimitiveDictObject._OBJ_CACHE.is_set: 144 | PrimitiveDictObject._OBJ_CACHE.restore(self) 145 | return 146 | 147 | add_primitive_fn(self, "at:", dict_at, ["index"]) 148 | add_primitive_fn(self, "clone", dict_clone, []) 149 | add_primitive_fn(self, "length", dict_length, []) 150 | add_primitive_fn(self, "at:Put:", dict_at_key_put_obj, ["key", "obj"]) 151 | 152 | PrimitiveDictObject._OBJ_CACHE.store(self) 153 | 154 | def __str__(self): 155 | return "dict()" 156 | 157 | def __eq__(self, obj): 158 | if not isinstance(obj, PrimitiveDictObject): 159 | return False 160 | 161 | return self.value == obj.value 162 | -------------------------------------------------------------------------------- /src/tinySelf/vm/primitives/primitive_false.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.vm.object_layout import Object 3 | from tinySelf.vm.primitives.add_primitive_fn import add_primitive_fn 4 | 5 | 6 | def is_false(interpreter, self, parameters): 7 | from tinySelf.vm.primitives.primitive_true import PrimitiveTrueObject 8 | obj = parameters[0] 9 | 10 | if isinstance(obj, PrimitiveFalseObjectSingleton): 11 | return PrimitiveTrueObject() 12 | else: 13 | return PrimitiveFalseObject() 14 | 15 | 16 | class PrimitiveFalseObjectSingleton(Object): 17 | def __init__(self): 18 | Object.__init__(self) 19 | 20 | add_primitive_fn(self, "is:", is_false, ["obj"]) 21 | 22 | def clone(self, copy_obj=None): 23 | return self 24 | 25 | def __str__(self): 26 | return "false" 27 | 28 | def __eq__(self, obj): 29 | return isinstance(obj, PrimitiveFalseObjectSingleton) 30 | 31 | 32 | _FALSE_OBJ = PrimitiveFalseObjectSingleton() 33 | def PrimitiveFalseObject(*args): 34 | return _FALSE_OBJ 35 | -------------------------------------------------------------------------------- /src/tinySelf/vm/primitives/primitive_float.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.vm.object_layout import Object 3 | 4 | from tinySelf.vm.primitives.cache import ObjCache 5 | from tinySelf.vm.primitives.primitive_true import PrimitiveTrueObject 6 | from tinySelf.vm.primitives.primitive_false import PrimitiveFalseObject 7 | from tinySelf.vm.primitives.primitive_str import PrimitiveStrObject 8 | from tinySelf.vm.primitives.add_primitive_fn import add_primitive_fn 9 | 10 | 11 | class _NumberObject(Object): 12 | _immutable_fields_ = ["value"] 13 | def __init__(self, obj_map=None): 14 | Object.__init__(self, obj_map) 15 | 16 | @property 17 | def float_value(self): 18 | return 0.0 19 | 20 | def result_type(self, val): 21 | return _NumberObject() 22 | 23 | @property 24 | def is_float(self): 25 | return False 26 | 27 | 28 | def add(interpreter, self, parameters): 29 | obj = parameters[0] 30 | 31 | # yeah, this can't be factored out, I've tried.. 32 | assert isinstance(self, PrimitiveFloatObject) 33 | assert isinstance(obj, _NumberObject) 34 | assert isinstance(self.value, float) 35 | 36 | return PrimitiveFloatObject(self.value + obj.float_value) 37 | 38 | 39 | def substract(interpreter, self, parameters): 40 | obj = parameters[0] 41 | assert isinstance(self, PrimitiveFloatObject) 42 | assert isinstance(obj, _NumberObject) 43 | assert isinstance(self.value, float) 44 | 45 | return PrimitiveFloatObject(self.value - obj.float_value) 46 | 47 | 48 | def multiply(interpreter, self, parameters): 49 | obj = parameters[0] 50 | assert isinstance(self, PrimitiveFloatObject) 51 | assert isinstance(obj, _NumberObject) 52 | assert isinstance(self.value, float) 53 | 54 | return PrimitiveFloatObject(self.value * obj.float_value) 55 | 56 | 57 | def divide(interpreter, self, parameters): 58 | obj = parameters[0] 59 | assert isinstance(self, PrimitiveFloatObject) 60 | assert isinstance(obj, _NumberObject) 61 | assert isinstance(self.value, float) 62 | 63 | return PrimitiveFloatObject(self.value / obj.float_value) 64 | 65 | 66 | def lt(interpreter, self, parameters): 67 | obj = parameters[0] 68 | assert isinstance(self, PrimitiveFloatObject) 69 | assert isinstance(obj, _NumberObject) 70 | assert isinstance(self.value, float) 71 | 72 | if self.value < obj.value: 73 | return PrimitiveTrueObject() 74 | else: 75 | return PrimitiveFalseObject() 76 | 77 | 78 | def lte(interpreter, self, parameters): 79 | obj = parameters[0] 80 | assert isinstance(self, PrimitiveFloatObject) 81 | assert isinstance(obj, _NumberObject) 82 | assert isinstance(self.value, float) 83 | 84 | if self.value <= obj.value: 85 | return PrimitiveTrueObject() 86 | else: 87 | return PrimitiveFalseObject() 88 | 89 | 90 | def gt(interpreter, self, parameters): 91 | obj = parameters[0] 92 | assert isinstance(self, PrimitiveFloatObject) 93 | assert isinstance(obj, _NumberObject) 94 | assert isinstance(self.value, float) 95 | 96 | if self.value > obj.value: 97 | return PrimitiveTrueObject() 98 | else: 99 | return PrimitiveFalseObject() 100 | 101 | 102 | def gte(interpreter, self, parameters): 103 | obj = parameters[0] 104 | assert isinstance(self, PrimitiveFloatObject) 105 | assert isinstance(obj, _NumberObject) 106 | assert isinstance(self.value, float) 107 | 108 | if self.value >= obj.value: 109 | return PrimitiveTrueObject() 110 | else: 111 | return PrimitiveFalseObject() 112 | 113 | 114 | def compare(interpreter, self, parameters): 115 | obj = parameters[0] 116 | assert isinstance(self, PrimitiveFloatObject) 117 | assert isinstance(obj, _NumberObject) 118 | assert isinstance(self.value, float) 119 | 120 | if self.value == obj.value: 121 | return PrimitiveTrueObject() 122 | else: 123 | return PrimitiveFalseObject() 124 | 125 | 126 | def as_string(interpreter, self, parameters): 127 | assert isinstance(self, PrimitiveFloatObject) 128 | 129 | # fix rpython's bug where str() on float object results in multiple zeros 130 | # at the end, which breaks unittests (2.450000 instead of 2.45) 131 | result = str(self.value) 132 | result = result.rstrip("0") 133 | if result.endswith("."): 134 | result += "0" 135 | 136 | return PrimitiveStrObject(result) 137 | 138 | 139 | def as_int(interpreter, self, parameters): 140 | from tinySelf.vm.primitives.primitive_int import PrimitiveIntObject 141 | 142 | assert isinstance(self, PrimitiveFloatObject) 143 | return PrimitiveIntObject(int(self.value)) 144 | 145 | 146 | class _FloatTraitObject(Object): 147 | pass 148 | 149 | 150 | FLOAT_TRAIT = _FloatTraitObject() 151 | 152 | 153 | class PrimitiveFloatObject(_NumberObject): 154 | _OBJ_CACHE = ObjCache() 155 | _immutable_fields_ = ["value"] 156 | def __init__(self, value, obj_map=None): 157 | _NumberObject.__init__(self, PrimitiveFloatObject._OBJ_CACHE.map) 158 | 159 | self.value = value 160 | 161 | if PrimitiveFloatObject._OBJ_CACHE.is_set: 162 | PrimitiveFloatObject._OBJ_CACHE.restore(self) 163 | return 164 | 165 | add_primitive_fn(self, "+", add, ["obj"]) 166 | add_primitive_fn(self, "-", substract, ["obj"]) 167 | add_primitive_fn(self, "*", multiply, ["obj"]) 168 | add_primitive_fn(self, "/", divide, ["obj"]) 169 | add_primitive_fn(self, "<", lt, ["obj"]) 170 | add_primitive_fn(self, "<=", lte, ["obj"]) 171 | add_primitive_fn(self, ">", gt, ["obj"]) 172 | add_primitive_fn(self, ">=", gte, ["obj"]) 173 | add_primitive_fn(self, "==", compare, ["obj"]) 174 | add_primitive_fn(self, "asString", as_string, []) 175 | add_primitive_fn(self, "asInt", as_int, []) 176 | 177 | self.meta_add_parent("trait", FLOAT_TRAIT) 178 | 179 | PrimitiveFloatObject._OBJ_CACHE.store(self) 180 | 181 | @property 182 | def float_value(self): 183 | return self.value 184 | 185 | def result_type(self, val): 186 | return PrimitiveFloatObject(val) 187 | 188 | def __eq__(self, obj): 189 | if not hasattr(obj, "value"): 190 | return False 191 | 192 | return self.value == obj.value 193 | 194 | def __str__(self): 195 | return str(self.value) 196 | -------------------------------------------------------------------------------- /src/tinySelf/vm/primitives/primitive_int.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.r_io import write 3 | 4 | from tinySelf.vm import Object 5 | from tinySelf.vm.primitives.cache import ObjCache 6 | from tinySelf.vm.primitives.primitive_str import PrimitiveStrObject 7 | from tinySelf.vm.primitives.primitive_true import PrimitiveTrueObject 8 | from tinySelf.vm.primitives.primitive_false import PrimitiveFalseObject 9 | from tinySelf.vm.primitives.primitive_float import _NumberObject 10 | from tinySelf.vm.primitives.primitive_float import PrimitiveFloatObject 11 | from tinySelf.vm.primitives.add_primitive_fn import add_primitive_fn 12 | 13 | 14 | def add(interpreter, self, parameters): 15 | obj = parameters[0] 16 | # yeah, this can't be factored out, I've tried.. 17 | assert isinstance(self, PrimitiveIntObject) 18 | assert isinstance(obj, _NumberObject) 19 | 20 | return obj.result_type(self.value + obj.value) 21 | 22 | 23 | def substract(interpreter, self, parameters): 24 | obj = parameters[0] 25 | assert isinstance(self, PrimitiveIntObject) 26 | assert isinstance(obj, _NumberObject) 27 | 28 | return obj.result_type(self.value - obj.value) 29 | 30 | 31 | def multiply(interpreter, self, parameters): 32 | obj = parameters[0] 33 | assert isinstance(self, PrimitiveIntObject) 34 | assert isinstance(obj, _NumberObject) 35 | 36 | return obj.result_type(self.value * obj.value) 37 | 38 | 39 | def divide(interpreter, self, parameters): 40 | obj = parameters[0] 41 | assert isinstance(self, PrimitiveIntObject) 42 | assert isinstance(obj, _NumberObject) 43 | 44 | if obj.value == 0: 45 | from tinySelf.vm.primitives.interpreter_primitives import primitive_fn_raise_error 46 | return primitive_fn_raise_error( 47 | interpreter, 48 | None, 49 | [PrimitiveStrObject("Division by zero.")] 50 | ) 51 | 52 | return obj.result_type(self.value / obj.value) 53 | 54 | 55 | def modulo(interpreter, self, parameters): 56 | obj = parameters[0] 57 | assert isinstance(self, PrimitiveIntObject) 58 | assert isinstance(obj, PrimitiveIntObject) 59 | 60 | return obj.result_type(int(self.value) % int(obj.value)) 61 | 62 | 63 | def lt(interpreter, self, parameters): 64 | obj = parameters[0] 65 | assert isinstance(self, PrimitiveIntObject) 66 | assert isinstance(obj, _NumberObject) 67 | 68 | if self.value < obj.value: 69 | return PrimitiveTrueObject() 70 | else: 71 | return PrimitiveFalseObject() 72 | 73 | 74 | def lte(interpreter, self, parameters): 75 | obj = parameters[0] 76 | assert isinstance(self, PrimitiveIntObject) 77 | assert isinstance(obj, _NumberObject) 78 | 79 | if self.value <= obj.value: 80 | return PrimitiveTrueObject() 81 | else: 82 | return PrimitiveFalseObject() 83 | 84 | 85 | def gt(interpreter, self, parameters): 86 | obj = parameters[0] 87 | assert isinstance(self, PrimitiveIntObject) 88 | assert isinstance(obj, _NumberObject) 89 | 90 | if self.value > obj.value: 91 | return PrimitiveTrueObject() 92 | else: 93 | return PrimitiveFalseObject() 94 | 95 | 96 | def gte(interpreter, self, parameters): 97 | obj = parameters[0] 98 | assert isinstance(self, PrimitiveIntObject) 99 | assert isinstance(obj, _NumberObject) 100 | 101 | if self.value >= obj.value: 102 | return PrimitiveTrueObject() 103 | else: 104 | return PrimitiveFalseObject() 105 | 106 | 107 | def compare(interpreter, self, parameters): 108 | obj = parameters[0] 109 | assert isinstance(self, PrimitiveIntObject) 110 | assert isinstance(obj, _NumberObject) 111 | 112 | if self.value == obj.value: 113 | return PrimitiveTrueObject() 114 | else: 115 | return PrimitiveFalseObject() 116 | 117 | 118 | def as_string(interpreter, self, parameters): 119 | assert isinstance(self, PrimitiveIntObject) 120 | return PrimitiveStrObject(str(self.value)) 121 | 122 | 123 | def as_float(interpreter, self, parameters): 124 | assert isinstance(self, PrimitiveIntObject) 125 | return PrimitiveFloatObject(float(self.value)) 126 | 127 | 128 | def print_int(interpreter, self, parameters): 129 | assert isinstance(self, PrimitiveIntObject) 130 | 131 | as_str = str(self.value) 132 | write(as_str) 133 | 134 | return PrimitiveStrObject(as_str) 135 | 136 | 137 | class _IntTraitObject(Object): 138 | pass 139 | 140 | 141 | INT_TRAIT = _IntTraitObject() 142 | 143 | 144 | class PrimitiveIntObject(_NumberObject): 145 | _OBJ_CACHE = ObjCache() 146 | _immutable_fields_ = ["value"] 147 | def __init__(self, value, obj_map=None): 148 | _NumberObject.__init__(self, PrimitiveIntObject._OBJ_CACHE.map) 149 | 150 | self.value = int(value) 151 | 152 | if PrimitiveIntObject._OBJ_CACHE.is_set: 153 | PrimitiveIntObject._OBJ_CACHE.restore(self) 154 | return 155 | 156 | add_primitive_fn(self, "+", add, ["obj"]) 157 | add_primitive_fn(self, "-", substract, ["obj"]) 158 | add_primitive_fn(self, "*", multiply, ["obj"]) 159 | add_primitive_fn(self, "/", divide, ["obj"]) 160 | add_primitive_fn(self, "%", modulo, ["obj"]) 161 | add_primitive_fn(self, "<", lt, ["obj"]) 162 | add_primitive_fn(self, "<=", lte, ["obj"]) 163 | add_primitive_fn(self, ">", gt, ["obj"]) 164 | add_primitive_fn(self, ">=", gte, ["obj"]) 165 | add_primitive_fn(self, "==", compare, ["obj"]) 166 | add_primitive_fn(self, "asString", as_string, []) 167 | add_primitive_fn(self, "asFloat", as_float, []) 168 | add_primitive_fn(self, "print", print_int, []) 169 | 170 | PrimitiveIntObject._OBJ_CACHE.store(self) 171 | 172 | @property 173 | def float_value(self): 174 | return float(self.value) 175 | 176 | def result_type(self, val): 177 | return PrimitiveIntObject(val) 178 | 179 | def __eq__(self, obj): 180 | if not hasattr(obj, "value"): 181 | return False 182 | 183 | return self.value == obj.value 184 | 185 | def __str__(self): 186 | return str(int(self.value)) 187 | -------------------------------------------------------------------------------- /src/tinySelf/vm/primitives/primitive_list.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.vm.object_layout import Object 3 | 4 | from tinySelf.vm.primitives.cache import ObjCache 5 | from tinySelf.vm.primitives.primitive_int import PrimitiveIntObject 6 | from tinySelf.vm.primitives.primitive_true import PrimitiveTrueObject 7 | from tinySelf.vm.primitives.primitive_false import PrimitiveFalseObject 8 | from tinySelf.vm.primitives.add_primitive_fn import add_primitive_fn 9 | 10 | 11 | def list_clone(interpreter, self, parameters): 12 | assert isinstance(self, PrimitiveListObject) 13 | 14 | return self._copy_with_primitives(PrimitiveListObject(self.value[:])) 15 | 16 | 17 | def list_append(interpreter, self, parameters): 18 | obj = parameters[0] 19 | assert isinstance(obj, Object) 20 | assert isinstance(self, PrimitiveListObject) 21 | 22 | self.value.append(obj) 23 | 24 | return obj 25 | 26 | 27 | def list_at(interpreter, self, parameters): 28 | obj = parameters[0] 29 | assert isinstance(obj, PrimitiveIntObject) 30 | assert isinstance(self, PrimitiveListObject) 31 | 32 | return self.value[int(obj.value)] 33 | 34 | 35 | def list_length(interpreter, self, parameters): 36 | assert isinstance(self, PrimitiveListObject) 37 | 38 | return PrimitiveIntObject(len(self.value)) 39 | 40 | 41 | def list_at_i_put_x(interpreter, self, parameters): 42 | index = parameters[0] 43 | obj = parameters[1] 44 | assert isinstance(index, PrimitiveIntObject) 45 | assert isinstance(obj, Object) 46 | assert isinstance(self, PrimitiveListObject) 47 | 48 | self.value[int(index.value)] = obj 49 | 50 | return obj 51 | 52 | 53 | def list_extend(interpreter, self, parameters): 54 | obj = parameters[0] 55 | assert isinstance(self, PrimitiveListObject) 56 | 57 | if isinstance(obj, PrimitiveListObject): 58 | self.value.extend(obj.value) 59 | return self 60 | else: 61 | raise ValueError("Not implemented yet!") # TODO: implement as call to tinySelf code 62 | 63 | 64 | def list_reversed(interpreter, self, parameters): 65 | assert isinstance(self, PrimitiveListObject) 66 | 67 | return PrimitiveListObject([x for x in reversed(self.value)]) 68 | 69 | 70 | def list_clear(interpreter, pseudo_self, parameters): 71 | assert isinstance(pseudo_self, PrimitiveListObject) 72 | 73 | pseudo_self.value = [] 74 | 75 | return pseudo_self 76 | 77 | 78 | class PrimitiveListObject(Object): 79 | _OBJ_CACHE = ObjCache() 80 | _immutable_fields_ = ["value"] 81 | def __init__(self, value, obj_map=None): 82 | Object.__init__(self, PrimitiveListObject._OBJ_CACHE.map) 83 | 84 | assert isinstance(value, list) 85 | self.value = value 86 | 87 | if PrimitiveListObject._OBJ_CACHE.is_set: 88 | PrimitiveListObject._OBJ_CACHE.restore(self) 89 | return 90 | 91 | add_primitive_fn(self, "at:", list_at, ["index"]) 92 | add_primitive_fn(self, "clone", list_clone, []) 93 | add_primitive_fn(self, "length", list_length, []) 94 | add_primitive_fn(self, "append:", list_append, ["obj"]) 95 | add_primitive_fn(self, "extend:", list_extend, ["obj"]) 96 | add_primitive_fn(self, "at:Put:", list_at_i_put_x, ["index", "obj"]) 97 | add_primitive_fn(self, "reversed", list_reversed, []) 98 | add_primitive_fn(self, "clear", list_clear, []) 99 | 100 | PrimitiveListObject._OBJ_CACHE.store(self) 101 | 102 | def __str__(self): 103 | # I would use something more elegant, but rpython doesn't let me.. 104 | items = "" 105 | for cnt, x in enumerate(self.value): 106 | items += x.__str__() 107 | 108 | if cnt < len(self.value) - 1: 109 | items += ", " 110 | 111 | return "[%s] asList" % items 112 | 113 | def __eq__(self, obj): 114 | if not isinstance(obj, PrimitiveListObject): 115 | return False 116 | 117 | return self.value == obj.value 118 | -------------------------------------------------------------------------------- /src/tinySelf/vm/primitives/primitive_nil.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.r_io import write 3 | 4 | from tinySelf.vm.object_layout import Object 5 | 6 | from tinySelf.vm.primitives.primitive_true import PrimitiveTrueObject 7 | from tinySelf.vm.primitives.primitive_false import PrimitiveFalseObject 8 | from tinySelf.vm.primitives.add_primitive_fn import add_primitive_fn 9 | 10 | 11 | def is_nil(interpreter, self, parameters): 12 | obj = parameters[0] 13 | 14 | if isinstance(obj, PrimitiveNilObjectSingleton): 15 | return PrimitiveTrueObject() 16 | else: 17 | return PrimitiveFalseObject() 18 | 19 | 20 | class PrimitiveNilObjectSingleton(Object): 21 | def __init__(self): 22 | Object.__init__(self) 23 | 24 | add_primitive_fn(self, "is:", is_nil, ["obj"]) 25 | 26 | def clone(self, copy_obj=None): 27 | return self 28 | 29 | def __str__(self): 30 | return "nil" 31 | 32 | def __eq__(self, obj): 33 | return isinstance(obj, PrimitiveNilObjectSingleton) 34 | 35 | 36 | _NIL_OBJ = PrimitiveNilObjectSingleton() 37 | def PrimitiveNilObject(*args): 38 | return _NIL_OBJ 39 | -------------------------------------------------------------------------------- /src/tinySelf/vm/primitives/primitive_str.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.r_io import write 3 | from tinySelf.r_io import writeln 4 | 5 | from tinySelf.vm.object_layout import Object 6 | 7 | from tinySelf.shared.string_repr import escape 8 | 9 | from tinySelf.vm.primitives.cache import ObjCache 10 | from tinySelf.vm.primitives.primitive_nil import PrimitiveNilObject 11 | from tinySelf.vm.primitives.add_primitive_fn import add_primitive_fn 12 | from tinySelf.vm.primitives.primitive_true import PrimitiveTrueObject 13 | from tinySelf.vm.primitives.primitive_true import PrimitiveFalseObject 14 | 15 | 16 | def add_strings(interpreter, self, parameters): 17 | obj = parameters[0] 18 | assert isinstance(obj, PrimitiveStrObject) 19 | assert isinstance(self, PrimitiveStrObject) 20 | assert isinstance(obj.value, str) 21 | assert isinstance(self.value, str) 22 | 23 | return PrimitiveStrObject(self.value + obj.value) 24 | 25 | 26 | def print_string(interpreter, self, parameters): 27 | assert isinstance(self, PrimitiveStrObject) 28 | assert isinstance(self.value, str) 29 | 30 | write(self.value) 31 | 32 | return PrimitiveNilObject() 33 | 34 | 35 | def print_string_newline(interpreter, self, parameters): 36 | assert isinstance(self, PrimitiveStrObject) 37 | assert isinstance(self.value, str) 38 | 39 | writeln(self.value) 40 | 41 | return PrimitiveNilObject() 42 | 43 | 44 | def string_endswith(interpreter, self, parameters): 45 | obj = parameters[0] 46 | assert isinstance(obj, PrimitiveStrObject) 47 | assert isinstance(self, PrimitiveStrObject) 48 | 49 | if self.value.endswith(obj.value): 50 | return PrimitiveTrueObject() 51 | else: 52 | return PrimitiveFalseObject() 53 | 54 | 55 | def string_startswith(interpreter, self, parameters): 56 | obj = parameters[0] 57 | assert isinstance(obj, PrimitiveStrObject) 58 | assert isinstance(self, PrimitiveStrObject) 59 | 60 | if self.value.startswith(obj.value): 61 | return PrimitiveTrueObject() 62 | else: 63 | return PrimitiveFalseObject() 64 | 65 | 66 | def string_contains(interpreter, self, parameters): 67 | obj = parameters[0] 68 | assert isinstance(obj, PrimitiveStrObject) 69 | assert isinstance(self, PrimitiveStrObject) 70 | 71 | if obj.value in self.value: 72 | return PrimitiveTrueObject() 73 | else: 74 | return PrimitiveFalseObject() 75 | 76 | 77 | def compare_strings(interpreter, self, parameters): 78 | obj = parameters[0] 79 | assert isinstance(obj, Object) 80 | assert isinstance(self, PrimitiveStrObject) 81 | 82 | if isinstance(obj, PrimitiveStrObject): 83 | if obj.value == self.value: 84 | return PrimitiveTrueObject() 85 | else: 86 | return PrimitiveFalseObject() 87 | 88 | # TODO: implement as call to tinySelf code 89 | raise ValueError("Can't yet compare other sequences with strings.") 90 | 91 | 92 | def as_string(interpreter, self, parameters): 93 | assert isinstance(self, PrimitiveStrObject) 94 | 95 | return self 96 | 97 | 98 | class _StrTraitObject(Object): 99 | pass 100 | 101 | 102 | STR_TRAIT = _StrTraitObject() 103 | 104 | 105 | class PrimitiveStrObject(Object): 106 | _OBJ_CACHE = ObjCache() 107 | _immutable_fields_ = ["value"] 108 | def __init__(self, value, obj_map=None): 109 | Object.__init__(self, PrimitiveStrObject._OBJ_CACHE.map) 110 | 111 | assert isinstance(value, str) 112 | self.value = value 113 | 114 | if PrimitiveStrObject._OBJ_CACHE.is_set: 115 | PrimitiveStrObject._OBJ_CACHE.restore(self) 116 | return 117 | 118 | add_primitive_fn(self, "+", add_strings, ["obj"]) 119 | add_primitive_fn(self, "print", print_string, []) 120 | add_primitive_fn(self, "printLine", print_string_newline, []) 121 | add_primitive_fn(self, "startsWith:", string_startswith, ["str"]) 122 | add_primitive_fn(self, "endsWith:", string_endswith, ["str"]) 123 | add_primitive_fn(self, "contains:", string_contains, ["str"]) 124 | add_primitive_fn(self, "asString", as_string, []) 125 | add_primitive_fn(self, "==", compare_strings, ["obj"]) 126 | 127 | self.meta_add_parent("trait", STR_TRAIT) 128 | 129 | PrimitiveStrObject._OBJ_CACHE.store(self) 130 | 131 | def __str__(self): 132 | return "'" + escape(self.value) + "'" 133 | 134 | def __eq__(self, obj): 135 | if not isinstance(obj, PrimitiveStrObject): 136 | return False 137 | 138 | return self.value == obj.value 139 | -------------------------------------------------------------------------------- /src/tinySelf/vm/primitives/primitive_time.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import time 3 | 4 | from tinySelf.vm.object_layout import Object 5 | from tinySelf.vm.primitives.add_primitive_fn import add_primitive_fn 6 | from tinySelf.vm.primitives.primitive_float import PrimitiveFloatObject 7 | 8 | 9 | def get_timestamp(interpreter, time_obj, parameters): 10 | return PrimitiveFloatObject(time.time()) 11 | 12 | 13 | def get_primitive_time_object(): 14 | time_obj = Object() 15 | add_primitive_fn(time_obj, "timestamp", get_timestamp, []) 16 | 17 | return time_obj 18 | -------------------------------------------------------------------------------- /src/tinySelf/vm/primitives/primitive_true.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.vm.object_layout import Object 3 | from tinySelf.vm.primitives.primitive_false import PrimitiveFalseObject 4 | from tinySelf.vm.primitives.add_primitive_fn import add_primitive_fn 5 | 6 | 7 | def is_true(interpreter, self, parameters): 8 | obj = parameters[0] 9 | 10 | if isinstance(obj, PrimitiveTrueObjectSingleton): 11 | return PrimitiveTrueObject() 12 | else: 13 | return PrimitiveFalseObject() 14 | 15 | 16 | class PrimitiveTrueObjectSingleton(Object): 17 | def __init__(self): 18 | Object.__init__(self) 19 | 20 | add_primitive_fn(self, "is:", is_true, ["obj"]) 21 | 22 | def clone(self, copy_obj=None): 23 | return self 24 | 25 | def __str__(self): 26 | return "true" 27 | 28 | def __eq__(self, obj): 29 | return isinstance(obj, PrimitiveTrueObjectSingleton) 30 | 31 | 32 | _TRUE_OBJ = PrimitiveTrueObjectSingleton() 33 | def PrimitiveTrueObject(*args): 34 | return _TRUE_OBJ 35 | -------------------------------------------------------------------------------- /src/tinySelf/vm/virtual_machine.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from rply import ParsingError 3 | 4 | from tinySelf.r_io import ewriteln 5 | from tinySelf.parser import lex_and_parse 6 | from tinySelf.parser import lex_and_parse_as_root 7 | from tinySelf.vm.primitives import get_primitives 8 | from tinySelf.vm.interpreter import Interpreter 9 | from tinySelf.vm.code_context import CodeContext 10 | from tinySelf.vm.object_layout import Object 11 | 12 | 13 | def run_stdlib(interpreter, stdlib_source, stdlib_path): 14 | stdlib_ast = lex_and_parse_as_root(stdlib_source) 15 | stdlib_process = interpreter.add_process(stdlib_ast.compile(), stdlib_path) 16 | 17 | interpreter.interpret() 18 | 19 | if stdlib_process.finished_with_error: 20 | ewriteln("Couldn't initialize stdlib:") 21 | ewriteln(stdlib_process.result.__str__()) 22 | ewriteln("\n") 23 | 24 | return False 25 | 26 | return True 27 | 28 | 29 | def virtual_machine(source, source_path, stdlib_source="", stdlib_path=""): 30 | universe = Object() 31 | universe.meta_add_slot("primitives", get_primitives()) 32 | 33 | interpreter = Interpreter(universe) 34 | 35 | if stdlib_source and not run_stdlib(interpreter, stdlib_source, stdlib_path): 36 | return None, interpreter 37 | 38 | ast = lex_and_parse_as_root(source) 39 | if not ast: 40 | return None, interpreter 41 | 42 | process = interpreter.add_process(ast.compile(), source_path) 43 | interpreter.interpret() 44 | 45 | return process, interpreter 46 | -------------------------------------------------------------------------------- /tests/jit.py: -------------------------------------------------------------------------------- 1 | from rpython import conftest 2 | class o: 3 | view = False 4 | viewloops = True 5 | conftest.option = o 6 | 7 | from rpython.jit.metainterp.test.support import LLJitMixin 8 | 9 | from tinySelf.parser import lex_and_parse_as_root 10 | from tinySelf.vm.primitives import get_primitives 11 | from tinySelf.vm.interpreter import Interpreter 12 | from tinySelf.vm.code_context import CodeContext 13 | from tinySelf.vm.object_layout import Object 14 | from tinySelf.vm.virtual_machine import run_stdlib 15 | import os 16 | 17 | stdlib_path = os.path.join(os.path.dirname(__file__), "../objects/stdlib.tself") 18 | 19 | with file(stdlib_path) as f: 20 | stdlib_source = f.read() 21 | 22 | 23 | class TestLLtype(LLJitMixin): 24 | def test_loop(self): 25 | source = """ 26 | (| 27 | benchmark = (| i <- 0. | 28 | [i < 1000] whileTrue: [ 29 | i: i + 1. 30 | ]. 31 | ). 32 | |) benchmark. 33 | """ 34 | universe = Object() 35 | universe.meta_add_slot("primitives", get_primitives()) 36 | 37 | interpreter = Interpreter(universe) 38 | run_stdlib(interpreter, stdlib_source, stdlib_path) 39 | 40 | ast = lex_and_parse_as_root(source) 41 | if not ast: 42 | return None, interpreter 43 | 44 | code = ast.compile(CodeContext()) 45 | interpreter.add_process(code.finalize()) 46 | def f(): 47 | interpreter.interpret() 48 | return 0 49 | #res = f() 50 | #assert res == 0 51 | res = self.meta_interp(f, [], listops=True, listcomp=True, backendopt=True) 52 | assert res == 0 53 | 54 | -------------------------------------------------------------------------------- /tests/scripts/count_to_1000000.self: -------------------------------------------------------------------------------- 1 | # Just simple benchmark file to quickly measure, whether the optimization had 2 | # any effect or not. 3 | 4 | (| 5 | benchmark = (| i <- 0. | 6 | [i < 1000000] whileTrue: [ 7 | i: i + 1. 8 | ]. 9 | ). 10 | 11 | run_benchmark = (| start_time. end_time. | 12 | start_time: primitives time timestamp. 13 | benchmark. 14 | end_time: primitives time timestamp. 15 | 16 | end_time - start_time asString + '\n' print. 17 | ). 18 | |) run_benchmark. 19 | -------------------------------------------------------------------------------- /tests/scripts/count_to_500.self: -------------------------------------------------------------------------------- 1 | (| 2 | test_while = (| i <- 0. | 3 | [ i < 500 ] whileTrue: [ 4 | 'i: ' print. 5 | i asString print. 6 | '\n' print. 7 | 'number of frames: ' print. 8 | primitives interpreter numberOfFrames asString print. 9 | '\n\n' print. 10 | 11 | i: i + 1. 12 | ]. 13 | ). 14 | |) test_while. 15 | -------------------------------------------------------------------------------- /tests/scripts/to_test_include.txt: -------------------------------------------------------------------------------- 1 | (| 2 | create_global_variable = (| universe_mirror | 3 | universe_mirror: primitives mirrorOn: universe. 4 | universe_mirror toSlot: 'run_script_flag' Add: 1. 5 | 'ok\n' print. 6 | ) 7 | |) create_global_variable 8 | -------------------------------------------------------------------------------- /tests/shared/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Bystroushaak/tinySelf/749e0f2b269d439b4d3477010463ef921548d5f6/tests/shared/__init__.py -------------------------------------------------------------------------------- /tests/shared/test_lightweight_dict.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import pytest 3 | 4 | from tinySelf.shared.lightweight_dict import LightWeightDict 5 | 6 | 7 | @pytest.fixture() 8 | def lwd(): 9 | return LightWeightDict() 10 | 11 | 12 | def test_lightweight_dict_one(lwd): 13 | lwd["xe"] = 1 14 | 15 | assert lwd["xe"] == 1 16 | 17 | with pytest.raises(KeyError): 18 | assert lwd["a"] 19 | 20 | assert lwd.get("a") is None 21 | assert lwd.get("a", 1) is 1 22 | 23 | assert lwd.keys() == ["xe"] 24 | assert lwd.values() == [1] 25 | 26 | lwd["xa"] = 2 27 | 28 | assert lwd["xa"] == 2 29 | assert lwd.keys() == ["xe", "xa"] 30 | assert lwd.values() == [1, 2] 31 | 32 | lwd["xo"] = 3 33 | assert lwd["xo"] == 3 34 | assert lwd.keys() == ["xe", "xa", "xo"] 35 | assert lwd.values() == [1, 2, 3] 36 | 37 | del lwd["xe"] 38 | 39 | with pytest.raises(KeyError): 40 | assert lwd["xe"] 41 | assert lwd.get("xe") is None 42 | 43 | assert lwd.keys() == ["xa", "xo"] 44 | assert lwd.values() == [2, 3] 45 | 46 | 47 | def test_lightweight_dict_four(lwd): 48 | lwd["a"] = 1 49 | lwd["b"] = 2 50 | lwd["c"] = 3 51 | lwd["d"] = 4 52 | 53 | assert lwd["a"] == 1 54 | assert lwd["b"] == 2 55 | assert lwd["c"] == 3 56 | assert lwd["d"] == 4 57 | 58 | with pytest.raises(KeyError): 59 | assert lwd["e"] 60 | 61 | assert lwd.keys() == ["a", "b", "c", "d"] 62 | assert lwd.values() == [1, 2, 3, 4] 63 | 64 | del lwd["b"] 65 | 66 | assert lwd["a"] == 1 67 | assert lwd["c"] == 3 68 | assert lwd["d"] == 4 69 | 70 | with pytest.raises(KeyError): 71 | assert lwd["e"] 72 | 73 | assert lwd.keys() == ["a", "c", "d"] 74 | assert lwd.values() == [1, 3, 4] 75 | 76 | 77 | def test_lightweight_dict_more(lwd): 78 | lwd._max_array_items = 4 79 | lwd["a"] = 1 80 | lwd["b"] = 2 81 | lwd["c"] = 3 82 | lwd["d"] = 4 83 | lwd["e"] = 5 84 | lwd["f"] = 6 85 | 86 | assert lwd["a"] == 1 87 | assert lwd["b"] == 2 88 | assert lwd["c"] == 3 89 | assert lwd["d"] == 4 90 | assert lwd["e"] == 5 91 | assert lwd["f"] == 6 92 | 93 | with pytest.raises(KeyError): 94 | assert lwd["g"] 95 | 96 | assert lwd.keys() == ["a", "b", "c", "d", "e", "f"] 97 | assert lwd.values() == [1, 2, 3, 4, 5, 6] 98 | 99 | del lwd["b"] 100 | 101 | assert lwd["a"] == 1 102 | assert lwd["c"] == 3 103 | assert lwd["d"] == 4 104 | assert lwd["e"] == 5 105 | assert lwd["f"] == 6 106 | 107 | with pytest.raises(KeyError): 108 | assert lwd["g"] 109 | 110 | assert lwd.keys() == ["a", "c", "d", "e", "f"] 111 | assert lwd.values() == [1, 3, 4, 5, 6] 112 | 113 | 114 | def test_lightweight_dict_set_multiple_times(lwd): 115 | lwd["a"] = 1 116 | assert lwd["a"] == 1 117 | 118 | lwd["a"] = 2 119 | assert lwd["a"] == 2 120 | 121 | 122 | def test_lightweight_dict_set_multiple_times_two_keys(lwd): 123 | lwd["a"] = 1 124 | lwd["b"] = 2 125 | assert lwd["a"] == 1 126 | assert lwd["b"] == 2 127 | 128 | lwd["b"] = 9 129 | lwd["a"] = 0 130 | assert lwd["a"] == 0 131 | assert lwd["b"] == 9 132 | -------------------------------------------------------------------------------- /tests/shared/test_linked_list_for_objects.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from pytest import raises 3 | from pytest import fixture 4 | 5 | from tinySelf.shared.linked_list_for_objects import LinkedListForObjects 6 | 7 | 8 | @fixture 9 | def lla() : 10 | return LinkedListForObjects() 11 | 12 | 13 | def test_init(lla): 14 | assert len(lla) == 0 15 | 16 | with raises(IndexError): 17 | lla[0] 18 | 19 | with raises(IndexError): 20 | lla[-1] 21 | 22 | 23 | def test_append(lla): 24 | lla.append(1) 25 | 26 | assert len(lla) == 1 27 | assert lla[0] == 1 28 | assert lla[-1] == 1 29 | 30 | with raises(IndexError): 31 | lla[1] 32 | 33 | 34 | def test_getitem_on_index_two(lla): 35 | lla.append(1) 36 | lla.append(2) 37 | 38 | assert len(lla) == 2 39 | assert lla[0] == 1 40 | assert lla[1] == 2 41 | assert lla[-1] == 2 42 | 43 | with raises(IndexError): 44 | lla[2] 45 | 46 | with raises(IndexError): 47 | lla[-2] 48 | 49 | 50 | def test_to_list_no_items(lla): 51 | assert lla.to_list() == [] 52 | 53 | 54 | def test_to_list_one_item(lla): 55 | lla.append(1) 56 | 57 | assert lla.to_list() == [1] 58 | 59 | 60 | def test_to_list_two_items(lla): 61 | lla.append(1) 62 | lla.append(2) 63 | 64 | assert lla.to_list() == [1, 2] 65 | 66 | 67 | def test_set_one_item(lla): 68 | with raises(IndexError): 69 | lla[0] = 1 70 | 71 | lla.append(1) 72 | lla[0] = 2 73 | 74 | assert lla[0] == 2 75 | assert lla[-1] == 2 76 | 77 | 78 | def test_set_two_items(lla): 79 | lla.append(1) 80 | lla.append(2) 81 | lla[0] = 9 82 | lla[1] = 0 83 | 84 | assert lla[0] == 9 85 | assert lla[1] == 0 86 | assert lla[-1] == 0 87 | 88 | 89 | def test_pop_first_empty(lla): 90 | with raises(IndexError): 91 | lla.pop_first() 92 | 93 | 94 | def test_pop_first_with_one_item(lla): 95 | lla.append(1) 96 | 97 | assert lla.pop_first() == 1 98 | assert lla.length == 0 99 | assert lla.to_list() == [] 100 | 101 | 102 | def test_pop_first_with_two_items(lla): 103 | lla.append(1) 104 | lla.append(2) 105 | 106 | assert lla.pop_first() == 1 107 | assert lla.pop_first() == 2 108 | assert len(lla) == 0 109 | 110 | 111 | def test_pop_last_with_empty(lla): 112 | with raises(IndexError): 113 | lla.pop_last() 114 | 115 | 116 | def test_pop_last_with_one_item(lla): 117 | lla.append(1) 118 | 119 | assert lla.pop_last() == 1 120 | assert lla.length == 0 121 | assert lla.to_list() == [] 122 | 123 | 124 | def test_pop_last_with_two_items(lla): 125 | lla.append(1) 126 | lla.append(2) 127 | 128 | assert lla.pop_last() == 2 129 | assert lla.pop_last() == 1 130 | assert len(lla) == 0 131 | -------------------------------------------------------------------------------- /tests/shared/test_string_repr.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.shared.string_repr import escape 3 | from tinySelf.shared.string_repr import unescape_esc_seq 4 | 5 | 6 | def test_unescape_esc_seq(): 7 | assert unescape_esc_seq(r"bleh") == "bleh" 8 | assert unescape_esc_seq(r"\n") == "\n" 9 | assert unescape_esc_seq(r"\t") == "\t" 10 | assert unescape_esc_seq(r"\'") == "'" 11 | assert unescape_esc_seq(r'\"') == '"' 12 | 13 | 14 | def test_escape(): 15 | assert escape('\n') == "\\n" 16 | assert escape('\t') == "\\t" 17 | assert escape('') == "" 18 | assert escape('asdas " asdasd') == "asdas \\\" asdasd" -------------------------------------------------------------------------------- /tests/shared/test_two_pointer_array.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from pytest import raises 3 | from pytest import fixture 4 | 5 | from tinySelf.shared.two_pointer_array import TwoPointerArray 6 | 7 | 8 | @fixture 9 | def tpa() : 10 | return TwoPointerArray(5) 11 | 12 | 13 | @fixture 14 | def tpa_3() : 15 | tpa_obj = TwoPointerArray(10) 16 | tpa_obj.append(1) 17 | tpa_obj.append(2) 18 | tpa_obj.append(3) 19 | 20 | return tpa_obj 21 | 22 | 23 | def test_twopointerarray_len(tpa, tpa_3): 24 | assert len(tpa) == 0 25 | assert len(tpa_3) == 3 26 | 27 | 28 | def test_twopointerarray_append_pop_last_pop_first(tpa): 29 | tpa.append(1) 30 | assert len(tpa) == 1 31 | assert tpa[0] == 1 32 | 33 | tpa.append(2) 34 | tpa.append(3) 35 | tpa.append(4) 36 | tpa.append(5) 37 | tpa.append(6) 38 | 39 | assert len(tpa) == 6 40 | assert tpa[0] == 1 41 | assert tpa[1] == 2 42 | assert tpa[2] == 3 43 | assert tpa[3] == 4 44 | assert tpa[4] == 5 45 | assert tpa[5] == 6 46 | 47 | assert tpa.pop_last() == 6 48 | assert len(tpa) == 5 49 | assert tpa[0] == 1 50 | assert tpa[1] == 2 51 | assert tpa[2] == 3 52 | assert tpa[3] == 4 53 | assert tpa[4] == 5 54 | 55 | assert tpa.pop_first() == 1 56 | assert len(tpa) == 4 57 | assert tpa[0] == 2 58 | assert tpa[1] == 3 59 | assert tpa[2] == 4 60 | assert tpa[3] == 5 61 | 62 | 63 | def test_tpa_to_list(tpa): 64 | tpa.append(1) 65 | tpa.append(2) 66 | tpa.append(3) 67 | tpa.append(4) 68 | 69 | assert tpa.pop_last() == 4 70 | assert tpa.pop_first() == 1 71 | 72 | assert tpa.to_list() == [2, 3] 73 | 74 | 75 | def test_tpa_to_list3(tpa_3): 76 | assert tpa_3.to_list() == [1, 2, 3] 77 | 78 | assert tpa_3.pop_last() == 3 79 | assert tpa_3.pop_first() == 1 80 | 81 | assert tpa_3.to_list() == [2] 82 | 83 | 84 | def test_get(tpa): 85 | with raises(IndexError): 86 | tpa[0] 87 | 88 | tpa.append(1) 89 | 90 | assert tpa[0] == 1 91 | 92 | with raises(IndexError): 93 | tpa[1] 94 | 95 | 96 | def test_reset(tpa): 97 | tpa.append(1) 98 | tpa.append(1) 99 | tpa.append(1) 100 | 101 | tpa.pop_first() 102 | tpa.pop_last() 103 | tpa.append(1) 104 | 105 | tpa.reset() 106 | 107 | assert tpa.to_list() == [] 108 | 109 | tpa.append(1) 110 | assert tpa.to_list() == [1] 111 | 112 | 113 | def test_iter(tpa): 114 | tpa.append(1) 115 | tpa.append(2) 116 | 117 | out = [] 118 | for i in tpa: 119 | out.append(i) 120 | 121 | assert out == [1, 2] 122 | -------------------------------------------------------------------------------- /tests/test_compiler.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.parser import ast_tokens 3 | from tinySelf.parser import lex_and_parse 4 | 5 | from tinySelf.vm.code_context import CodeContext 6 | 7 | 8 | def test_object_compilation(): 9 | ast = lex_and_parse(""" 10 | (| 11 | a = (| :var | var printLine. var). 12 | b <- nil. 13 | | nil.) 14 | """) 15 | 16 | assert len(ast) == 1 17 | 18 | context = CodeContext() 19 | 20 | ast[0].compile(context) 21 | -------------------------------------------------------------------------------- /tests/test_lexer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from rply import Token 3 | 4 | from tinySelf.parser.lexer import lexer 5 | 6 | 7 | def test_int_numbers(): 8 | assert list(lexer.lex("2")) == [ 9 | Token('NUMBER', "2"), 10 | ] 11 | 12 | assert list(lexer.lex("0")) == [ 13 | Token('NUMBER', "0"), 14 | ] 15 | 16 | assert list(lexer.lex("-1")) == [ 17 | Token('NUMBER', "-1"), 18 | ] 19 | 20 | 21 | def test_int_hexa_numbers(): 22 | assert list(lexer.lex("0x25")) == [ 23 | Token('NUMBER', "0x25"), 24 | ] 25 | 26 | 27 | def test_float_numbers(): 28 | assert list(lexer.lex("1.1")) == [ 29 | Token('NUMBER', "1.1"), 30 | ] 31 | 32 | assert list(lexer.lex("2.3572719819")) == [ 33 | Token('NUMBER', "2.3572719819"), 34 | ] 35 | 36 | assert list(lexer.lex("-0.3")) == [ 37 | Token('NUMBER', "-0.3"), 38 | ] 39 | 40 | 41 | def test_single_q_string(): 42 | assert list(lexer.lex("'hello'")) == [ 43 | Token('SINGLE_Q_STRING', "'hello'"), 44 | ] 45 | 46 | assert list(lexer.lex("'hello \\' quote'")) == [ 47 | Token('SINGLE_Q_STRING', "'hello \\' quote'"), 48 | ] 49 | 50 | assert list(lexer.lex("'a \\' b' 'c \\' d'")) == [ 51 | Token('SINGLE_Q_STRING', r"'a \' b'"), 52 | Token('SINGLE_Q_STRING', r"'c \' d'"), 53 | ] 54 | 55 | assert list(lexer.lex("'hello \n quote'")) == [ 56 | Token('SINGLE_Q_STRING', "'hello \n quote'"), 57 | ] 58 | 59 | 60 | def test_double_q_string(): 61 | assert list(lexer.lex('"hello"')) == [ 62 | Token('DOUBLE_Q_STRING', '"hello"'), 63 | ] 64 | 65 | assert list(lexer.lex('"hello \\" quote"')) == [ 66 | Token('DOUBLE_Q_STRING', '"hello \\" quote"'), 67 | ] 68 | 69 | assert list(lexer.lex('"a \\" b" "c \\" d"')) == [ 70 | Token('DOUBLE_Q_STRING', r'"a \" b"'), 71 | Token('DOUBLE_Q_STRING', r'"c \" d"'), 72 | ] 73 | 74 | assert list(lexer.lex('"hello \n quote"')) == [ 75 | Token('DOUBLE_Q_STRING', '"hello \n quote"'), 76 | ] 77 | 78 | 79 | def test_identifier(): 80 | assert list(lexer.lex('identifier')) == [ 81 | Token('IDENTIFIER', 'identifier'), 82 | ] 83 | 84 | assert list(lexer.lex('iDentIfier ID2')) == [ 85 | Token('IDENTIFIER', 'iDentIfier'), 86 | Token('IDENTIFIER', 'ID2'), 87 | ] 88 | 89 | assert list(lexer.lex('p*')) == [ 90 | Token('IDENTIFIER', 'p*'), 91 | ] 92 | 93 | 94 | 95 | def test_argument(): 96 | assert list(lexer.lex(':argument')) == [ 97 | Token('ARGUMENT', ':argument'), 98 | ] 99 | 100 | assert list(lexer.lex('"string" :argument idenTifier')) == [ 101 | Token('DOUBLE_Q_STRING', '"string"'), 102 | Token('ARGUMENT', ':argument'), 103 | Token('IDENTIFIER', 'idenTifier'), 104 | ] 105 | 106 | 107 | def test_kw_identifier(): 108 | assert list(lexer.lex('kwArgument: i')) == [ 109 | Token('FIRST_KW', 'kwArgument:'), 110 | Token('IDENTIFIER', 'i'), 111 | ] 112 | 113 | 114 | def test_kw(): 115 | assert list(lexer.lex('kwArgument: i KeyWord: kw')) == [ 116 | Token('FIRST_KW', 'kwArgument:'), 117 | Token('IDENTIFIER', 'i'), 118 | Token('KEYWORD', 'KeyWord:'), 119 | Token('IDENTIFIER', 'kw'), 120 | ] 121 | 122 | 123 | def test_complex(): 124 | assert list(lexer.lex('(kwArgument: i KeyWord: [id])')) == [ 125 | Token('OBJ_START', '('), 126 | Token('FIRST_KW', 'kwArgument:'), 127 | Token('IDENTIFIER', 'i'), 128 | Token('KEYWORD', 'KeyWord:'), 129 | Token("BLOCK_START", '['), 130 | Token('IDENTIFIER', 'id'), 131 | Token("BLOCK_END", ']'), 132 | Token('OBJ_END', ')'), 133 | ] 134 | 135 | 136 | def test_operator(): 137 | assert list(lexer.lex('!')) == [ 138 | Token('OPERATOR', '!'), 139 | ] 140 | 141 | assert list(lexer.lex('!@$%&*-+~/?<>,')) == [ 142 | Token('OPERATOR', '!@$%&*-+~/?<>,') 143 | ] 144 | 145 | assert list(lexer.lex('! @ $ % & * - + ~ / ? < > ,')) == [ 146 | Token('OPERATOR', '!'), 147 | Token('OPERATOR', '@'), 148 | Token('OPERATOR', '$'), 149 | Token('OPERATOR', '%'), 150 | Token('OPERATOR', '&'), 151 | Token('OPERATOR', '*'), 152 | Token('OPERATOR', '-'), 153 | Token('OPERATOR', '+'), 154 | Token('OPERATOR', '~'), 155 | Token('OPERATOR', '/'), 156 | Token('OPERATOR', '?'), 157 | Token('OPERATOR', '<'), 158 | Token('OPERATOR', '>'), 159 | Token('OPERATOR', ','), 160 | ] 161 | 162 | assert list(lexer.lex('==')) == [ 163 | Token('OPERATOR', '==') 164 | ] 165 | 166 | assert list(lexer.lex('===')) == [ 167 | Token('OPERATOR', '===') 168 | ] 169 | 170 | def test_return(): 171 | assert list(lexer.lex('^')) == [ 172 | Token('RETURN', '^'), 173 | ] 174 | 175 | assert list(lexer.lex('^xe.')) == [ 176 | Token('RETURN', '^'), 177 | Token('IDENTIFIER', 'xe'), 178 | Token('END_OF_EXPR', '.'), 179 | ] 180 | 181 | 182 | def test_end_of_expression(): 183 | assert list(lexer.lex('.')) == [ 184 | Token('END_OF_EXPR', '.'), 185 | ] 186 | 187 | assert list(lexer.lex('obj message.')) == [ 188 | Token('IDENTIFIER', 'obj'), 189 | Token('IDENTIFIER', 'message'), 190 | Token('END_OF_EXPR', '.'), 191 | ] 192 | 193 | 194 | def test_separator(): 195 | assert list(lexer.lex('|')) == [ 196 | Token('SEPARATOR', '|'), 197 | ] 198 | 199 | assert list(lexer.lex('(|var| obj message.)')) == [ 200 | Token('OBJ_START', '('), 201 | Token('SEPARATOR', '|'), 202 | Token('IDENTIFIER', 'var'), 203 | Token('SEPARATOR', '|'), 204 | Token('IDENTIFIER', 'obj'), 205 | Token('IDENTIFIER', 'message'), 206 | Token('END_OF_EXPR', '.'), 207 | Token('OBJ_END', ')'), 208 | 209 | ] 210 | 211 | 212 | def test_comment(): 213 | assert list(lexer.lex('#\n')) == [ 214 | Token('COMMENT', '#\n'), 215 | ] 216 | 217 | assert list(lexer.lex('obj message. # comment \n id #')) == [ 218 | Token('IDENTIFIER', 'obj'), 219 | Token('IDENTIFIER', 'message'), 220 | Token('END_OF_EXPR', '.'), 221 | Token('COMMENT', '# comment \n'), 222 | Token('IDENTIFIER', 'id'), 223 | Token('COMMENT', '#'), 224 | ] 225 | 226 | 227 | def test_cascade(): 228 | assert list(lexer.lex(';')) == [ 229 | Token('CASCADE', ';'), 230 | ] 231 | 232 | assert list(lexer.lex('obj message; message2.')) == [ 233 | Token('IDENTIFIER', 'obj'), 234 | Token('IDENTIFIER', 'message'), 235 | Token('CASCADE', ';'), 236 | Token('IDENTIFIER', 'message2'), 237 | Token('END_OF_EXPR', '.'), 238 | ] 239 | 240 | 241 | def test_assingment_op(): 242 | assert list(lexer.lex('=')) == [ 243 | Token('ASSIGNMENT', '='), 244 | ] 245 | 246 | 247 | def test_rw_assingment_op(): 248 | assert list(lexer.lex('<-')) == [ 249 | Token('RW_ASSIGNMENT', '<-'), 250 | ] 251 | 252 | 253 | def test_self_keyword(): 254 | assert list(lexer.lex('self')) == [ 255 | Token('SELF', 'self'), 256 | ] 257 | -------------------------------------------------------------------------------- /tests/vm/debug/test_pretty_print_ast.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.vm.debug.pretty_print_ast import pretty_print_ast 3 | 4 | 5 | def test_pretty_print_ast(): 6 | source = """Send(obj=Object(slots={test_while_100: Object(slots={i: \ 7 | IntNumber(0), i:: AssignmentPrimitive()}, code=[Send(obj=Block(code=[Send(obj=\ 8 | Send(obj=Self(), msg=Message(i)), msg=BinaryMessage(name=<, parameter=IntNumber\ 9 | (1)))]), msg=KeywordMessage(name=whileTrue:, parameters=[Block(code=[Send(obj=\ 10 | Self(), msg=KeywordMessage(name=i:, parameters=[Send(obj=Send(obj=Self(), \ 11 | msg=Message(i)), msg=BinaryMessage(name=+, parameter=IntNumber(1)))])), \ 12 | Send(obj=Self(), msg=Message(false))])])), Send(obj=Send(obj=Send(obj=Self(), \ 13 | msg=Message(i)), msg=Message(asString)), msg=Message(printLine))]), test_another: \ 14 | Object(slots={x: Nil(), x:: AssignmentPrimitive(), brekeke: Nil(), brekeke:: \ 15 | AssignmentPrimitive()}, code=[Send(obj=Self(), msg=KeywordMessage(name=x:, \ 16 | parameters=[IntNumber(0)])), Send(obj='another', msg=Message(printLine)), \ 17 | Send(obj=Self(), msg=Message(brekeke)), Send(obj=Block(code=[Send(obj=Send(obj=\ 18 | Self(), msg=Message(x)), msg=BinaryMessage(name=<, parameter=IntNumber(2)))]), \ 19 | msg=KeywordMessage(name=whileTrue:, parameters=[Block(code=[Send(obj=Self(), \ 20 | msg=KeywordMessage(name=x:, parameters=[Send(obj=Send(obj=Self(), msg=Message(x)), \ 21 | msg=BinaryMessage(name=+, parameter=IntNumber(1)))])), Send(obj=Self(), \ 22 | msg=Message(nil))])])), Send(obj=Send(obj=Send(obj=Self(), msg=Message(x)), \ 23 | msg=Message(asString)), msg=Message(printLine))]), test_while: \ 24 | Object(code=[Send(obj=Self(), msg=Message(test_while_100)), Send(obj=Self(), \ 25 | msg=Message(test_another))])}), msg=Message(test_while))""" 26 | 27 | assert pretty_print_ast(source) == """Send( 28 | obj=Object( 29 | slots={ 30 | test_while_100: Object( 31 | slots={ 32 | i: IntNumber(0), 33 | i:: AssignmentPrimitive() 34 | }, 35 | code=[ 36 | Send( 37 | obj=Block( 38 | code=[ 39 | Send( 40 | obj=Send( 41 | obj=Self(), 42 | msg=Message(i) 43 | ), 44 | msg=BinaryMessage( 45 | name=<, 46 | parameter=IntNumber(1) 47 | ) 48 | ) 49 | ] 50 | ), 51 | msg=KeywordMessage( 52 | name=whileTrue:, 53 | parameters=[ 54 | Block( 55 | code=[ 56 | Send( 57 | obj=Self(), 58 | msg=KeywordMessage( 59 | name=i:, 60 | parameters=[ 61 | Send( 62 | obj=Send( 63 | obj=Self(), 64 | msg=Message(i) 65 | ), 66 | msg=BinaryMessage( 67 | name=+, 68 | parameter=IntNumber(1) 69 | ) 70 | ) 71 | ] 72 | ) 73 | ), 74 | Send( 75 | obj=Self(), 76 | msg=Message(false) 77 | ) 78 | ] 79 | ) 80 | ] 81 | ) 82 | ), 83 | Send( 84 | obj=Send( 85 | obj=Send( 86 | obj=Self(), 87 | msg=Message(i) 88 | ), 89 | msg=Message(asString) 90 | ), 91 | msg=Message(printLine) 92 | ) 93 | ] 94 | ), 95 | test_another: Object( 96 | slots={ 97 | x: Nil(), 98 | x:: AssignmentPrimitive(), 99 | brekeke: Nil(), 100 | brekeke:: AssignmentPrimitive() 101 | }, 102 | code=[ 103 | Send( 104 | obj=Self(), 105 | msg=KeywordMessage( 106 | name=x:, 107 | parameters=[ 108 | IntNumber(0) 109 | ] 110 | ) 111 | ), 112 | Send( 113 | obj='another', 114 | msg=Message(printLine) 115 | ), 116 | Send( 117 | obj=Self(), 118 | msg=Message(brekeke) 119 | ), 120 | Send( 121 | obj=Block( 122 | code=[ 123 | Send( 124 | obj=Send( 125 | obj=Self(), 126 | msg=Message(x) 127 | ), 128 | msg=BinaryMessage( 129 | name=<, 130 | parameter=IntNumber(2) 131 | ) 132 | ) 133 | ] 134 | ), 135 | msg=KeywordMessage( 136 | name=whileTrue:, 137 | parameters=[ 138 | Block( 139 | code=[ 140 | Send( 141 | obj=Self(), 142 | msg=KeywordMessage( 143 | name=x:, 144 | parameters=[ 145 | Send( 146 | obj=Send( 147 | obj=Self(), 148 | msg=Message(x) 149 | ), 150 | msg=BinaryMessage( 151 | name=+, 152 | parameter=IntNumber(1) 153 | ) 154 | ) 155 | ] 156 | ) 157 | ), 158 | Send( 159 | obj=Self(), 160 | msg=Message(nil) 161 | ) 162 | ] 163 | ) 164 | ] 165 | ) 166 | ), 167 | Send( 168 | obj=Send( 169 | obj=Send( 170 | obj=Self(), 171 | msg=Message(x) 172 | ), 173 | msg=Message(asString) 174 | ), 175 | msg=Message(printLine) 176 | ) 177 | ] 178 | ), 179 | test_while: Object( 180 | code=[ 181 | Send( 182 | obj=Self(), 183 | msg=Message(test_while_100) 184 | ), 185 | Send( 186 | obj=Self(), 187 | msg=Message(test_another) 188 | ) 189 | ] 190 | ) 191 | } 192 | ), 193 | msg=Message(test_while) 194 | )""" -------------------------------------------------------------------------------- /tests/vm/primitives/test_mirror.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.vm.primitives import Mirror 3 | from tinySelf.vm.primitives import PrimitiveIntObject 4 | from tinySelf.vm.primitives import PrimitiveStrObject 5 | 6 | from tinySelf.vm.object_layout import Object 7 | 8 | 9 | def test_mirror(): 10 | o = Object() 11 | m = Mirror(o) 12 | 13 | assert not o.slot_lookup("v")[1] 14 | 15 | _, add_primitive = m.slot_lookup("toSlot:Add:") 16 | assert add_primitive.map.primitive_code 17 | result = add_primitive.map.primitive_code( 18 | None, 19 | m, 20 | [PrimitiveStrObject("v"), PrimitiveIntObject(1)] 21 | ) 22 | assert result == o 23 | 24 | assert o.slot_lookup("v")[1] == PrimitiveIntObject(1) 25 | -------------------------------------------------------------------------------- /tests/vm/primitives/test_primitive_false.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.vm.primitives import PrimitiveIntObject 3 | from tinySelf.vm.primitives import PrimitiveTrueObject 4 | from tinySelf.vm.primitives import PrimitiveFalseObject 5 | 6 | 7 | def test_PrimitiveFalseObject(): 8 | assert PrimitiveFalseObject() 9 | 10 | 11 | def test_PrimitiveFalseObject_is(): 12 | o = PrimitiveFalseObject() 13 | 14 | _, is_slot = o.slot_lookup("is:") 15 | result = is_slot.map.primitive_code(None, o, [PrimitiveFalseObject()]) 16 | assert result == PrimitiveTrueObject() 17 | 18 | _, is_slot = o.slot_lookup("is:") 19 | result = is_slot.map.primitive_code(None, o, [PrimitiveIntObject(3)]) 20 | assert result == PrimitiveFalseObject() 21 | 22 | _, is_slot = o.slot_lookup("is:") 23 | result = is_slot.map.primitive_code(None, o, [PrimitiveTrueObject()]) 24 | assert result == PrimitiveFalseObject() 25 | -------------------------------------------------------------------------------- /tests/vm/primitives/test_primitive_float.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import pytest 3 | 4 | from tinySelf.parser import lex_and_parse 5 | from tinySelf.vm.interpreter import Interpreter 6 | from tinySelf.vm.code_context import CodeContext 7 | 8 | from tinySelf.vm.primitives import get_primitives 9 | from tinySelf.vm.primitives import PrimitiveIntObject 10 | from tinySelf.vm.primitives import PrimitiveFloatObject 11 | from tinySelf.vm.primitives import PrimitiveTrueObject 12 | from tinySelf.vm.primitives import PrimitiveFalseObject 13 | 14 | 15 | 16 | 17 | def test_instanciation(): 18 | obj = PrimitiveFloatObject(3.14) 19 | assert obj 20 | 21 | 22 | def test_instanciation_with_int(): 23 | obj = PrimitiveFloatObject(1) 24 | assert obj 25 | 26 | 27 | def test_float_and_int(): 28 | ast = lex_and_parse("""(| 29 | add = (|| 30 | 1.5 + 1 31 | ) 32 | |) add""") 33 | 34 | context = ast[0].compile(CodeContext()) 35 | interpreter = Interpreter(universe=get_primitives(), code_context=context) 36 | 37 | interpreter.interpret() 38 | assert interpreter.process.result == PrimitiveFloatObject(2.5) 39 | 40 | 41 | def test_int_and_float(): 42 | ast = lex_and_parse("""(| 43 | add = (|| 44 | 1 + 1.5 45 | ) 46 | |) add""") 47 | 48 | context = ast[0].compile(CodeContext()) 49 | interpreter = Interpreter(universe=get_primitives(), code_context=context) 50 | 51 | interpreter.interpret() 52 | assert interpreter.process.result == PrimitiveFloatObject(2.5) 53 | -------------------------------------------------------------------------------- /tests/vm/primitives/test_primitive_int.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import pytest 3 | 4 | from tinySelf.vm.primitives import PrimitiveIntObject 5 | from tinySelf.vm.primitives import PrimitiveStrObject 6 | from tinySelf.vm.primitives import PrimitiveTrueObject 7 | from tinySelf.vm.primitives import PrimitiveFalseObject 8 | 9 | 10 | def call_primitive_int_binary_op(first, op, second, equals): 11 | o = PrimitiveIntObject(first) 12 | 13 | if isinstance(equals, (int, long)): 14 | equals = PrimitiveIntObject(equals) 15 | 16 | _, primitive_slot = o.slot_lookup(op) 17 | assert primitive_slot.map.primitive_code 18 | result = primitive_slot.map.primitive_code(None, o, [PrimitiveIntObject(second)]) 19 | assert result == equals 20 | 21 | 22 | def test_PrimitiveIntObject_plus(): 23 | call_primitive_int_binary_op(3, "+", 2, equals=5) 24 | 25 | 26 | def test_PrimitiveIntObject_minus(): 27 | call_primitive_int_binary_op(3, "-", 2, equals=1) 28 | 29 | 30 | def test_PrimitiveIntObject_minus_negative_result(): 31 | call_primitive_int_binary_op(3, "-", 4, equals=-1) 32 | 33 | 34 | def test_PrimitiveIntObject_multiply(): 35 | call_primitive_int_binary_op(3, "*", 2, equals=6) 36 | 37 | 38 | def test_PrimitiveIntObject_divide(): 39 | call_primitive_int_binary_op(6, "/", 2, equals=3) 40 | 41 | 42 | def test_PrimitiveIntObject_modulo(): 43 | call_primitive_int_binary_op(4, "%", 3, equals=1) 44 | 45 | 46 | def test_PrimitiveIntObject_greater(): 47 | call_primitive_int_binary_op(4, ">", 3, equals=PrimitiveTrueObject()) 48 | call_primitive_int_binary_op(1, ">", 5, equals=PrimitiveFalseObject()) 49 | call_primitive_int_binary_op(1, ">", 1, equals=PrimitiveFalseObject()) 50 | 51 | 52 | def test_PrimitiveIntObject_lower(): 53 | call_primitive_int_binary_op(1, "<", 5, equals=PrimitiveTrueObject()) 54 | call_primitive_int_binary_op(5, "<", 1, equals=PrimitiveFalseObject()) 55 | call_primitive_int_binary_op(1, "<", 1, equals=PrimitiveFalseObject()) 56 | 57 | 58 | def test_PrimitiveIntObject_greater_or_equal(): 59 | call_primitive_int_binary_op(5, ">=", 1, equals=PrimitiveTrueObject()) 60 | call_primitive_int_binary_op(1, ">=", 5, equals=PrimitiveFalseObject()) 61 | call_primitive_int_binary_op(1, ">=", 1, equals=PrimitiveTrueObject()) 62 | 63 | 64 | def test_PrimitiveIntObject_lower_or_equal(): 65 | call_primitive_int_binary_op(1, "<=", 5, equals=PrimitiveTrueObject()) 66 | call_primitive_int_binary_op(5, "<=", 1, equals=PrimitiveFalseObject()) 67 | call_primitive_int_binary_op(1, "<=", 1, equals=PrimitiveTrueObject()) 68 | 69 | 70 | def test_PrimitiveIntObject_as_primitive_string(): 71 | o = PrimitiveIntObject(2) 72 | 73 | _, plus_slot = o.slot_lookup("asString") 74 | assert plus_slot.map.primitive_code 75 | result = plus_slot.map.primitive_code(None, o, []) 76 | assert result == PrimitiveStrObject("2") -------------------------------------------------------------------------------- /tests/vm/primitives/test_primitive_nil.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.vm.primitives import PrimitiveIntObject 3 | from tinySelf.vm.primitives import PrimitiveNilObject 4 | from tinySelf.vm.primitives import PrimitiveTrueObject 5 | from tinySelf.vm.primitives import PrimitiveFalseObject 6 | 7 | 8 | def test_PrimitiveNilObject(): 9 | assert PrimitiveNilObject() 10 | 11 | 12 | def test_PrimitiveNilObject_is(): 13 | o = PrimitiveNilObject() 14 | 15 | _, is_slot = o.slot_lookup("is:") 16 | result = is_slot.map.primitive_code(None, o, [PrimitiveNilObject()]) 17 | assert result == PrimitiveTrueObject() 18 | 19 | _, is_slot = o.slot_lookup("is:") 20 | result = is_slot.map.primitive_code(None, o, [PrimitiveIntObject(3)]) 21 | assert result == PrimitiveFalseObject() 22 | 23 | _, is_slot = o.slot_lookup("is:") 24 | result = is_slot.map.primitive_code(None, o, [PrimitiveTrueObject()]) 25 | assert result == PrimitiveFalseObject() 26 | -------------------------------------------------------------------------------- /tests/vm/primitives/test_primitive_true.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.vm.primitives import PrimitiveIntObject 3 | from tinySelf.vm.primitives import PrimitiveTrueObject 4 | from tinySelf.vm.primitives import PrimitiveFalseObject 5 | 6 | 7 | def test_PrimitiveTrueObject(): 8 | assert PrimitiveTrueObject() 9 | 10 | 11 | def test_PrimitiveTrueObject_is(): 12 | o = PrimitiveTrueObject() 13 | 14 | _, is_slot = o.slot_lookup("is:") 15 | result = is_slot.map.primitive_code(None, o, [PrimitiveTrueObject()]) 16 | assert result == PrimitiveTrueObject() 17 | 18 | _, is_slot = o.slot_lookup("is:") 19 | result = is_slot.map.primitive_code(None, o, [PrimitiveIntObject(3)]) 20 | assert result == PrimitiveFalseObject() 21 | 22 | _, is_slot = o.slot_lookup("is:") 23 | result = is_slot.map.primitive_code(None, o, [PrimitiveFalseObject()]) 24 | assert result == PrimitiveFalseObject() 25 | -------------------------------------------------------------------------------- /tests/vm/test_frames.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from pytest import raises 3 | from pytest import fixture 4 | 5 | from tinySelf.vm.frames import MethodStack 6 | from tinySelf.vm.frames import ProcessStack 7 | from tinySelf.vm.frames import ProcessCycler 8 | from tinySelf.vm.code_context import CodeContext 9 | 10 | from tinySelf.vm.code_context import CodeContext 11 | from tinySelf.vm.object_layout import Object 12 | 13 | from tinySelf.vm.primitives import PrimitiveIntObject 14 | from tinySelf.vm.interpreter import NIL 15 | 16 | 17 | @fixture 18 | def code_context_with_literals(): 19 | cc = CodeContext() 20 | cc.literals = [1, 2] 21 | 22 | return cc 23 | 24 | 25 | def test_method_stack(code_context_with_literals): 26 | f = MethodStack(code_context_with_literals) 27 | f.push(PrimitiveIntObject(1)) 28 | f.push(PrimitiveIntObject(2)) 29 | 30 | assert f.pop() == PrimitiveIntObject(2) 31 | assert f.pop() == PrimitiveIntObject(1) 32 | 33 | with raises(IndexError): 34 | f.pop() 35 | 36 | assert f.pop_or_nil() == NIL 37 | f.push(PrimitiveIntObject(1)) 38 | assert f.pop_or_nil() == PrimitiveIntObject(1) 39 | assert f.pop_or_nil() == NIL 40 | 41 | 42 | def test_process_stack(code_context_with_literals): 43 | ps = ProcessStack(code_context_with_literals) 44 | 45 | assert not ps.is_nested_call() 46 | assert ps.frame is ps.top_frame() 47 | 48 | ps.pop_frame() 49 | assert ps.frame 50 | 51 | ps.pop_frame_down() 52 | assert ps.frame 53 | 54 | ps.pop_down_and_cleanup_frame() 55 | assert ps.frame 56 | 57 | 58 | def test_process_stack_push_frame_behavior(code_context_with_literals): 59 | ps = ProcessStack(code_context_with_literals) 60 | ps.frame.push(PrimitiveIntObject(1)) 61 | 62 | assert not ps.is_nested_call() 63 | 64 | cc = CodeContext() 65 | method = Object() 66 | ps.push_frame(cc, method) 67 | 68 | assert ps.is_nested_call() 69 | assert ps.frame.code_context == cc 70 | 71 | retval = Object() 72 | ps.frame.push(retval) 73 | 74 | assert ps.length == 2 75 | stack = list(ps.frame) 76 | assert stack[-1] == retval 77 | 78 | ps.pop_down_and_cleanup_frame() 79 | 80 | assert ps.length == 1 81 | stack = list(ps.frame) 82 | assert stack[-1] == retval 83 | assert stack[-2] != retval 84 | assert ps.frame.prev_stack is None 85 | 86 | 87 | def test_process_cycler(): 88 | c1 = CodeContext() 89 | pc = ProcessCycler(c1) 90 | 91 | assert pc.has_processes_to_run() 92 | 93 | pc.next_process() 94 | assert pc.process.frame.code_context is c1 95 | 96 | pc.next_process() 97 | assert pc.process.frame.code_context is c1 98 | 99 | 100 | def test_process_cycler_multiple_processes(): 101 | c1 = CodeContext() 102 | c2 = CodeContext() 103 | pc = ProcessCycler() 104 | 105 | assert not pc.has_processes_to_run() 106 | 107 | pc.add_process(c1) 108 | pc.add_process(c2) 109 | 110 | assert pc.has_processes_to_run() 111 | 112 | assert pc.process.frame.code_context is c1 113 | pc.next_process() 114 | assert pc.process.frame.code_context is c1 115 | pc.next_process() 116 | assert pc.process.frame.code_context is c2 117 | pc.next_process() 118 | assert pc.process.frame.code_context is c1 119 | pc.next_process() 120 | assert pc.process.frame.code_context is c2 121 | 122 | 123 | def test_process_cycler_multiple_processes_remove_process(): 124 | c1 = CodeContext() 125 | c2 = CodeContext() 126 | pc = ProcessCycler() 127 | 128 | pc.add_process(c1) 129 | pc.add_process(c2) 130 | 131 | assert pc.process.frame.code_context is c1 132 | pc.next_process() 133 | assert pc.process.frame.code_context is c1 134 | pc.next_process() 135 | assert pc.process.frame.code_context is c2 136 | pc.next_process() 137 | assert pc.process.frame.code_context is c1 138 | pc.next_process() 139 | assert pc.process.frame.code_context is c2 140 | 141 | pc.remove_active_process() 142 | 143 | assert pc.process.frame.code_context is c1 144 | -------------------------------------------------------------------------------- /tests/vm/test_interpreter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os.path 3 | 4 | from pytest import raises 5 | 6 | from tinySelf.parser import lex_and_parse 7 | 8 | from tinySelf.vm.primitives import get_primitives 9 | from tinySelf.vm.primitives import PrimitiveIntObject 10 | from tinySelf.vm.primitives import PrimitiveStrObject 11 | 12 | from tinySelf.vm.interpreter import Interpreter 13 | 14 | from tinySelf.vm.code_context import CodeContext 15 | 16 | from tinySelf.vm.virtual_machine import virtual_machine 17 | 18 | 19 | 20 | def test_interpreter(): 21 | ast = lex_and_parse("""(| 22 | a <- 1. 23 | add: b = (|| 24 | (a + 1) + b 25 | ) 26 | |) add: 2""") 27 | 28 | context = ast[0].compile(CodeContext()) 29 | interpreter = Interpreter(universe=get_primitives(), code_context=context) 30 | 31 | interpreter.interpret() 32 | assert interpreter.process.result == PrimitiveIntObject(4) 33 | 34 | 35 | def test_assignment_primitive(): 36 | ast = lex_and_parse("""(| 37 | a <- 0. 38 | incA = (|| 39 | a: 1. 40 | ^a 41 | ) 42 | |) incA""") 43 | 44 | context = ast[0].compile(CodeContext()) 45 | interpreter = Interpreter(universe=get_primitives(), code_context=context) 46 | 47 | interpreter.interpret() 48 | assert interpreter.process.result == PrimitiveIntObject(1) 49 | 50 | 51 | def test_block(): 52 | ast = lex_and_parse("""(| 53 | a <- 0. 54 | addTenToBlk: blk = (|| 55 | 10 + (blk value). 56 | ). 57 | 58 | add = (| tmp = 3. tmp_block. | 59 | tmp_block: [a + tmp]. 60 | a: 1. # block should reflect current scopes 61 | 2 + addTenToBlk: tmp_block. 62 | ) 63 | |) add""") 64 | 65 | context = ast[0].compile(CodeContext()) 66 | interpreter = Interpreter(universe=get_primitives(), code_context=context) 67 | 68 | interpreter.interpret() 69 | assert interpreter.process.result == PrimitiveIntObject(16) 70 | 71 | 72 | def test_block_with_argument(): 73 | ast = lex_and_parse("""(| 74 | a <- 0. 75 | giveOneToBlock: blk = (|| 76 | blk with: 1 77 | ). 78 | 79 | shouldBeThree = (| tmp = 1. block. | 80 | block: [| :x | a + tmp + x]. 81 | a: 1. 82 | giveOneToBlock: block. 83 | ) 84 | |) shouldBeThree""") 85 | 86 | context = ast[0].compile(CodeContext()) 87 | interpreter = Interpreter(universe=get_primitives(), code_context=context) 88 | 89 | interpreter.interpret() 90 | assert interpreter.process.result == PrimitiveIntObject(3) 91 | 92 | 93 | def test_calling_block_twice_with_argument(): 94 | ast = lex_and_parse("""(| 95 | a <- 0. 96 | giveOneToBlock: blk = (|| 97 | blk with: 1 98 | ). 99 | 100 | giveTwoToBlock: blk = (|| 101 | blk with: 2 102 | ). 103 | 104 | shouldBeThree = (| tmp = 1. block. sub_result. | 105 | block: [| :x | a + tmp + x]. 106 | a: 1. 107 | sub_result: giveOneToBlock: block. 108 | ^ sub_result + giveTwoToBlock: block 109 | ) 110 | |) shouldBeThree""") 111 | 112 | context = ast[0].compile(CodeContext()) 113 | interpreter = Interpreter(universe=get_primitives(), code_context=context) 114 | 115 | interpreter.interpret() 116 | assert interpreter.process.result == PrimitiveIntObject(7) 117 | 118 | 119 | def test_resend(): 120 | ast = lex_and_parse("""(| 121 | p* = (| xex = 1. |). 122 | q* = (| xex = 2. |). 123 | 124 | fetchXex = (|| q.xex) 125 | |) fetchXex""") 126 | 127 | context = ast[0].compile(CodeContext()) 128 | interpreter = Interpreter(universe=get_primitives(), code_context=context) 129 | 130 | interpreter.interpret() 131 | assert interpreter.process.result == PrimitiveIntObject(2) 132 | 133 | 134 | def test_resend_is_in_local_context(): 135 | ast = lex_and_parse("""(| 136 | p* = (| xex = (|| ^1). |). 137 | q* = (| xex = (|| a + 1). |). 138 | a = 1. 139 | 140 | fetchXex = (|| q.xex) 141 | |) fetchXex""") 142 | 143 | context = ast[0].compile(CodeContext()) 144 | interpreter = Interpreter(universe=get_primitives(), code_context=context) 145 | 146 | interpreter.interpret() 147 | assert interpreter.process.result == PrimitiveIntObject(2) 148 | 149 | 150 | def test_resend_keyword(): 151 | ast = lex_and_parse("""(| 152 | p* = (| xex: a = (|| 1). |). 153 | q* = (| xex: a = (|| a). |). 154 | 155 | fetchXex = (|| q.xex: 2) 156 | |) fetchXex""") 157 | 158 | context = ast[0].compile(CodeContext()) 159 | interpreter = Interpreter(universe=get_primitives(), code_context=context) 160 | 161 | interpreter.interpret() 162 | assert interpreter.process.result == PrimitiveIntObject(2) 163 | 164 | 165 | def test_parallelism(): 166 | one_plus_one_ast = lex_and_parse("""(| 167 | add = (|| 1 + 1) 168 | |) add""") 169 | two_plus_two_ast = lex_and_parse("""(| 170 | add = (|| 2 + 2) 171 | |) add""") 172 | 173 | one_plus_one_ctx = one_plus_one_ast[0].compile(CodeContext()) 174 | two_plus_two_ctx = two_plus_two_ast[0].compile(CodeContext()) 175 | 176 | interpreter = Interpreter(universe=get_primitives()) 177 | one_plus_one_process = interpreter.add_process(one_plus_one_ctx) 178 | two_plus_two_process = interpreter.add_process(two_plus_two_ctx) 179 | 180 | assert not one_plus_one_process.finished 181 | assert not one_plus_one_process.result 182 | assert not two_plus_two_process.finished 183 | assert not two_plus_two_process.result 184 | 185 | interpreter.interpret() 186 | 187 | assert one_plus_one_process.finished 188 | assert one_plus_one_process.result == PrimitiveIntObject(2) 189 | assert two_plus_two_process.finished 190 | assert two_plus_two_process.result == PrimitiveIntObject(4) 191 | 192 | 193 | def test_halt(): 194 | ast = lex_and_parse("""(| 195 | test = (|| 196 | primitives interpreter halt: 'Test' 197 | ) 198 | |) test""") 199 | 200 | context = ast[0].compile(CodeContext()) 201 | interpreter = Interpreter(universe=get_primitives(), code_context=context) 202 | 203 | interpreter.interpret() 204 | assert interpreter.process.finished 205 | assert not interpreter.process.finished_with_error 206 | assert interpreter.process.result == PrimitiveStrObject("Test") 207 | 208 | 209 | def test_setting_of_error_handler_works(): 210 | ast = lex_and_parse("""(| 211 | test = (|| 212 | primitives interpreter setErrorHandler: [:obj. :stack | 213 | obj printString. 214 | ] 215 | ) 216 | |) test""") 217 | 218 | context = ast[0].compile(CodeContext()) 219 | interpreter = Interpreter(universe=get_primitives(), code_context=context) 220 | 221 | interpreter.interpret() 222 | 223 | 224 | def test_unhandled_error(): 225 | ast = lex_and_parse("""(| 226 | test = (|| 227 | primitives interpreter error: 'Test' 228 | ) 229 | |) test""") 230 | 231 | interpreter = Interpreter(universe=get_primitives()) 232 | process = interpreter.add_process(ast[0].compile(CodeContext())) 233 | 234 | interpreter.interpret() 235 | assert process.finished 236 | assert process.finished_with_error 237 | assert process.result == PrimitiveStrObject("Test") 238 | 239 | 240 | def test_set_error_handler_and_handle_error(): 241 | ast = lex_and_parse("""(| 242 | raiseError = (|| primitives interpreter error: 'Test'.). 243 | test = (|| 244 | primitives interpreter setErrorHandler: [:msg. :err | 245 | primitives interpreter restoreProcess: err With: 1. 246 | ]. 247 | ^ 1 + raiseError. 248 | ) 249 | |) test""") 250 | 251 | context = ast[0].compile(CodeContext()) 252 | interpreter = Interpreter(universe=get_primitives(), code_context=context) 253 | 254 | interpreter.interpret() 255 | assert interpreter.process.finished 256 | assert not interpreter.process.finished_with_error 257 | result = interpreter.process.result 258 | assert result == PrimitiveIntObject(2) 259 | 260 | 261 | def test_double_return_from_block(): 262 | source = """(| 263 | init_true = (| true_mirror | 264 | true_mirror: primitives mirrorOn: true. 265 | true_mirror toSlot: 'ifFalse:' Add: (| :blck | nil.). 266 | ). 267 | init_false = (| false_mirror | 268 | false_mirror: primitives mirrorOn: false. 269 | false_mirror toSlot: 'ifFalse:' Add: (| :blck | blck value.). 270 | ). 271 | 272 | test_double_return: blck = (| did_run <- false. | 273 | [ did_run: true. ^ blck value ] value. 274 | 275 | did_run ifFalse: [ 276 | primitives interpreter error: 'Block did not run.'. 277 | ]. 278 | 279 | primitives interpreter error: 'Block did not returned.'. 280 | 281 | ^ 0. 282 | ). 283 | 284 | run = (| d | 285 | init_true. 286 | init_false. 287 | 288 | ((test_double_return: [ 1 ]) == 1) ifFalse: [ 289 | primitives interpreter error: 'Bad value returned from test_double_return.'. 290 | ]. 291 | ). 292 | |) run""" 293 | 294 | process, interpreter = virtual_machine(source, "none") 295 | 296 | assert process.finished 297 | assert not process.finished_with_error, process.result.__str__() 298 | 299 | 300 | def test_running_self_unittest_file(): 301 | dirname = os.path.dirname(__file__) 302 | source_file_path = os.path.join(dirname, "../scripts/unittest.self") 303 | stdlib_file_path = os.path.join(dirname, "../../objects/stdlib.tself") 304 | 305 | with open(source_file_path) as source_file: 306 | with open(stdlib_file_path) as stdlib_file: 307 | process, interpreter = virtual_machine( 308 | source_file.read(), 309 | source_file_path, 310 | stdlib_file.read(), 311 | stdlib_file_path 312 | ) 313 | 314 | assert process.finished 315 | assert not process.finished_with_error, process.result.__str__() 316 | -------------------------------------------------------------------------------- /tests/vm/test_object_layout_object.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.vm.primitives import PrimitiveStrObject 3 | from tinySelf.vm.primitives import AssignmentPrimitive 4 | from tinySelf.vm.code_context import CodeContext 5 | from tinySelf.vm.object_layout import Object 6 | 7 | 8 | def test_meta_add_slot(): 9 | val = PrimitiveStrObject("xe") 10 | 11 | o = Object() 12 | assert not o._slot_values 13 | 14 | o.meta_add_slot("test", val) 15 | assert o._slot_values[0] == val 16 | 17 | 18 | def test_meta_add_slot_dont_check_duplicates(): 19 | xx = PrimitiveStrObject("xx") 20 | zz = PrimitiveStrObject("zz") 21 | 22 | o = Object() 23 | assert not o._slot_values 24 | 25 | o.meta_add_slot("xx", xx) 26 | o.meta_add_slot("zz", zz) 27 | assert len(o._slot_values) == 2 28 | 29 | o.meta_add_slot("xx2", xx) 30 | assert len(o._slot_values) == 3 31 | 32 | 33 | # def test_meta_add_slot_do_check_duplicates(): 34 | # xx = PrimitiveStrObject("xx") 35 | # zz = PrimitiveStrObject("zz") 36 | # 37 | # o = Object() 38 | # assert not o._slot_values 39 | # 40 | # o.meta_add_slot("xx", xx) 41 | # o.meta_add_slot("zz", zz) 42 | # assert len(o._slot_values) == 2 43 | # 44 | # o.meta_add_slot("xx2", xx, check_duplicates=True) 45 | # assert len(o._slot_values) == 2 46 | 47 | 48 | def test_set_slot(): 49 | o = Object() 50 | 51 | o.meta_add_slot("test", PrimitiveStrObject("xe")) 52 | assert o.set_slot("test", PrimitiveStrObject("xax")) 53 | assert not o.set_slot("bad_slot", PrimitiveStrObject("x")) 54 | 55 | 56 | def test_get_slot(): 57 | o = Object() 58 | 59 | val = PrimitiveStrObject("xe") 60 | o.meta_add_slot("test", val) 61 | assert o.get_slot("test") is val 62 | 63 | 64 | def test_get_slot_missing(): 65 | assert Object().get_slot("crap") is None 66 | 67 | 68 | def test_meta_remove_slot(): 69 | o = Object() 70 | assert not o._slot_values 71 | 72 | o.meta_add_slot("test", Object()) 73 | assert o._slot_values 74 | assert "test" in o.map._slots 75 | 76 | o.meta_remove_slot("test") 77 | assert not o._slot_values 78 | assert "test" not in o.map._slots 79 | 80 | 81 | def test_meta_remove_missing_slot(): 82 | o = Object() 83 | 84 | o.meta_add_slot("test", Object()) 85 | assert not o.meta_remove_slot("x") 86 | 87 | 88 | def test_meta_remove_slot_shift_map_pointers(): 89 | first = PrimitiveStrObject("first") 90 | second = PrimitiveStrObject("second") 91 | third = PrimitiveStrObject("third") 92 | 93 | o = Object() 94 | assert not o._slot_values 95 | 96 | o.meta_add_slot("first", first) 97 | o.meta_add_slot("second", second) 98 | o.meta_add_slot("third", third) 99 | 100 | assert o.get_slot("first") is first 101 | assert o.get_slot("second") is second 102 | assert o.get_slot("third") is third 103 | 104 | o.meta_remove_slot("first") 105 | 106 | assert len(o._slot_values) == 2 107 | assert len(o.map._slots) == 2 108 | assert o.map._slots["second"] == 0 109 | assert o.map._slots["third"] == 1 110 | 111 | assert o.get_slot("first") is None 112 | assert o.get_slot("second") == second 113 | assert o.get_slot("third") == third 114 | 115 | 116 | def test_meta_insert_slot(): 117 | first = PrimitiveStrObject("first") 118 | second = PrimitiveStrObject("second") 119 | third = PrimitiveStrObject("third") 120 | 121 | o = Object() 122 | assert not o._slot_values 123 | 124 | o.meta_add_slot("first", first) 125 | o.meta_add_slot("third", third) 126 | 127 | assert o.get_slot("first") is first 128 | assert o.get_slot("third") is third 129 | 130 | o.meta_insert_slot(1, "second", second) 131 | assert o.map._slots.keys() == ["first", "second", "third"] 132 | 133 | # make sure that objects didn't shifted 134 | assert o.get_slot("first") is first 135 | assert o.get_slot("second") is second 136 | assert o.get_slot("third") is third 137 | 138 | 139 | def test_clone(): 140 | o = Object() 141 | o.meta_add_slot("test", Object()) 142 | 143 | # clones share same map 144 | clone = o.clone() 145 | assert clone.map is o.map 146 | assert clone._slot_values == o._slot_values 147 | 148 | # clones with updated slot value share same map 149 | clone.set_slot("test", Object()) 150 | assert clone.map is o.map 151 | assert clone._slot_values != o._slot_values 152 | 153 | # clones with different structure don't share maps 154 | clone.meta_add_slot("another", Object()) 155 | assert clone.map is not o.map 156 | assert clone._slot_values != o._slot_values 157 | 158 | 159 | def test_meta_add_parent(): 160 | val = Object() 161 | 162 | o = Object() 163 | o.meta_add_parent("p*", val) 164 | 165 | assert "p*" in o.map._parent_slots 166 | assert val in o._parent_slot_values 167 | 168 | 169 | def test_meta_add_parent_cloned_objects_use_same_map(): 170 | o = Object() 171 | o.meta_add_parent("a", PrimitiveStrObject("value")) 172 | x = o.clone() 173 | 174 | assert "a" in o.parent_slot_keys 175 | 176 | assert o.map == x.map 177 | 178 | 179 | def test_meta_add_parent_cloned_objects_dont_change_when_parent_is_changed(): 180 | o = Object() 181 | o.meta_add_parent("a", PrimitiveStrObject("value")) 182 | x = o.clone() 183 | 184 | assert o.map == x.map 185 | 186 | x.meta_add_parent("a", PrimitiveStrObject("another")) 187 | 188 | assert o.map == x.map 189 | assert o != x 190 | 191 | 192 | def test_meta_add_parent_structural_change_creates_new_map_add(): 193 | o = Object() 194 | o.meta_add_parent("a", PrimitiveStrObject("value")) 195 | x = o.clone() 196 | 197 | assert o.map == x.map 198 | 199 | x.meta_add_parent("*", PrimitiveStrObject("another")) 200 | assert o.map != x.map 201 | assert o != x 202 | 203 | 204 | def test_meta_add_parent_structural_change_creates_new_map_remove(): 205 | o = Object() 206 | o.meta_add_parent("a", PrimitiveStrObject("value")) 207 | x = o.clone() 208 | 209 | assert o.map == x.map 210 | 211 | x.meta_remove_parent("a") 212 | 213 | assert o.map != x.map 214 | assert o != x 215 | assert "a" in o.parent_slot_keys 216 | 217 | 218 | def test_meta_remove_parent(): 219 | a_slot = PrimitiveStrObject("value a") 220 | b_slot = PrimitiveStrObject("value b") 221 | 222 | o = Object() 223 | o.meta_add_parent("a", a_slot) 224 | o.meta_add_parent("b", b_slot) 225 | 226 | assert "a" in o.parent_slot_keys 227 | assert "b" in o.parent_slot_keys 228 | 229 | assert o.meta_get_parent("a") is a_slot 230 | assert o.meta_get_parent("b") is b_slot 231 | 232 | o.meta_remove_parent("a") 233 | 234 | assert o.meta_get_parent("b") is b_slot 235 | assert len(o._parent_slot_values) == 1 236 | 237 | 238 | def test_get_slot_from_one_parent(): 239 | val = PrimitiveStrObject("it is xex!") 240 | 241 | p = Object() 242 | p.meta_add_slot("xex", val) 243 | 244 | o = Object() 245 | o.meta_add_parent("p", p) 246 | 247 | assert o.get_slot("xex") is None 248 | assert o.parent_lookup("xex") is val 249 | 250 | 251 | def test_get_slot_from_several_parents(): 252 | """ 253 | o.parents 254 | | 255 | |-- p <-- cycle, yay --, 256 | | |-- x -> Object() | 257 | | |-- y -> Object() | 258 | | `-- z -> p --------' 259 | | 260 | `-- p3 261 | `-- x -> p2 262 | | 263 | `-- xex 264 | """ 265 | val = PrimitiveStrObject("it is xex!") 266 | 267 | p = Object() 268 | p.meta_add_slot("x", Object()) 269 | p.meta_add_slot("y", Object()) 270 | p.meta_add_slot("z", p) # cycle, yay! 271 | 272 | p2 = Object() 273 | p.meta_add_slot("xex", val) 274 | 275 | p3 = Object() 276 | p.meta_add_slot("x", p2) 277 | 278 | o = Object() 279 | o.meta_add_parent("p", p) 280 | o.meta_add_parent("p3", p3) 281 | 282 | assert o.get_slot("xex") is None 283 | assert o.parent_lookup("xex") is val 284 | 285 | 286 | def test_parent_lookup_from_parent_tree(): 287 | """ 288 | Based on real case. 289 | """ 290 | value = PrimitiveStrObject("value") 291 | 292 | o1 = Object() 293 | o2 = Object() 294 | o3 = o2.clone() 295 | o4 = Object() 296 | o5 = Object() 297 | 298 | o1.scope_parent = o2 299 | 300 | o2.scope_parent = o3 301 | o2.meta_add_parent("*", o5) 302 | 303 | o3.scope_parent = o4 304 | o3.meta_add_parent("*", o5) 305 | 306 | o4.scope_parent = o5 307 | o4.meta_add_slot("value", value) 308 | 309 | assert o1.get_slot("value") is None 310 | assert o1.parent_lookup("value") is value 311 | 312 | 313 | def slot_lookup(): 314 | val = PrimitiveStrObject("it is xex!") 315 | flat = PrimitiveStrObject("it is flat") 316 | 317 | p = Object() 318 | p.meta_add_slot("xex", val) 319 | 320 | o = Object() 321 | o.meta_add_parent("p", p) 322 | o.meta_add_parent("p", flat) 323 | 324 | assert o.get_slot("xex") is None 325 | assert o.slot_lookup("xex")[1] is val 326 | assert o.slot_lookup("flat")[1] is flat 327 | 328 | 329 | def test_slot_lookup_from_scope_parent(): 330 | p = Object() 331 | val = PrimitiveStrObject("it is xex!") 332 | p.meta_add_slot("xex", val) 333 | 334 | o = Object() 335 | o.scope_parent = p 336 | 337 | assert o.get_slot("xex") is None 338 | assert o.slot_lookup("xex")[1] is val 339 | 340 | 341 | def test_slot_lookup_from_scope_parent_and_then_parents(): 342 | p = Object() 343 | val = PrimitiveStrObject("it is xex!") 344 | p.meta_add_slot("a", val) 345 | 346 | interobj = Object() 347 | interobj.scope_parent = p 348 | 349 | o = Object() 350 | o.scope_parent = Object() 351 | o.scope_parent.meta_add_parent("*", interobj) 352 | 353 | assert o.slot_lookup("a")[1] is val 354 | 355 | 356 | def test_has_code(): 357 | o = Object() 358 | assert not o.has_code 359 | 360 | o.map.code_context = CodeContext() 361 | assert o.has_code 362 | 363 | 364 | def test_has_primitive_code(): 365 | o = Object() 366 | assert not o.has_primitive_code 367 | 368 | def test(x): 369 | return x 370 | 371 | o.map.primitive_code = test 372 | assert o.has_primitive_code 373 | assert not o.is_assignment_primitive 374 | 375 | 376 | def test_is_assignment_primitive(): 377 | o = Object() 378 | assert not o.is_assignment_primitive 379 | 380 | o = AssignmentPrimitive() 381 | assert o.is_assignment_primitive 382 | assert not o.has_code 383 | 384 | -------------------------------------------------------------------------------- /tests/vm/test_object_layout_object_map.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from tinySelf.vm.object_layout import Object 3 | from tinySelf.vm.object_layout import Block 4 | from tinySelf.vm.object_layout import ObjectMap 5 | from tinySelf.vm.code_context import CodeContext 6 | 7 | 8 | def test_create_instance(): 9 | assert ObjectMap() 10 | 11 | 12 | def test_add_slot(): 13 | om = ObjectMap() 14 | 15 | om.add_slot("test", 1) 16 | assert "test" in om._slots 17 | assert om._slots["test"] == 1 18 | 19 | 20 | def test_remove_slot(): 21 | om = ObjectMap() 22 | 23 | om.add_slot("test", 1) 24 | assert "test" in om._slots 25 | 26 | om.remove_slot("test") 27 | assert "test" not in om._slots 28 | 29 | om.remove_slot("azgabash") 30 | 31 | 32 | def test_insert_slot(): 33 | om = ObjectMap() 34 | 35 | om.add_slot("first", 1) 36 | om.add_slot("third", 1) 37 | assert om._slots.keys() == ["first", "third"] 38 | 39 | om.insert_slot(1, "second", 1) 40 | assert om._slots.keys() == ["first", "second", "third"] 41 | 42 | om.insert_slot(0, "zero", 1) 43 | assert om._slots.keys() == ["zero", "first", "second", "third"] 44 | 45 | om.insert_slot(10, "tenth", 1) 46 | assert om._slots.keys() == ["zero", "first", "second", "third", "tenth"] 47 | 48 | om.insert_slot(-1, "-1", 1) 49 | assert om._slots.keys() == ["-1", "zero", "first", "second", "third", "tenth"] 50 | 51 | 52 | def test_set_or_add_parent(): 53 | om = ObjectMap() 54 | om.add_parent("test", 1) 55 | 56 | assert "test" in om._parent_slots 57 | assert om._parent_slots["test"] == 1 58 | 59 | 60 | def test_remove_parent(): 61 | om = ObjectMap() 62 | 63 | om.add_parent("test", 1) 64 | assert "test" in om._parent_slots 65 | 66 | om.remove_parent("test") 67 | assert "test" not in om._parent_slots 68 | 69 | 70 | def test_clone(): 71 | om = ObjectMap() 72 | om.code_context = CodeContext() 73 | om.add_slot("xex", 1) 74 | 75 | cloned = om.clone() 76 | assert cloned._slots == om._slots 77 | assert cloned._slots is not om._slots 78 | 79 | 80 | def test_clone_is_block(): 81 | o = Block() 82 | 83 | o.meta_add_parent("xe", Object()) # creates new map 84 | 85 | assert o.is_block 86 | -------------------------------------------------------------------------------- /utils/debug_dump.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | 5 | from tinySelf.parser import lex_and_parse 6 | from tinySelf.vm.primitives import get_primitives 7 | from tinySelf.vm.interpreter import Interpreter 8 | from tinySelf.vm.code_context import CodeContext 9 | from tinySelf.vm.object_layout import Object 10 | 11 | 12 | if __name__ == '__main__': 13 | universe = Object() 14 | universe.meta_add_slot("primitives", get_primitives()) 15 | 16 | with open(sys.argv[1]) as f: 17 | ast = lex_and_parse(f.read()) 18 | 19 | if not ast: 20 | sys.exit(0) 21 | 22 | interpreter = Interpreter(universe) 23 | 24 | try: 25 | for item in ast: 26 | process = interpreter.add_process(item.compile(CodeContext())) 27 | interpreter.interpret() 28 | print process.result.__str__() 29 | print process.finished_with_error 30 | except Exception as e: 31 | print "Caught %s; %s" % (e.__class__.__name__, str(e)) 32 | -------------------------------------------------------------------------------- /utils/open_windows.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | import os.path 3 | 4 | import sh 5 | 6 | 7 | if __name__ == '__main__': 8 | tinySelf_root_path = os.path.join(os.path.dirname(__file__), "../") 9 | tinySelf_root_path = os.path.abspath(tinySelf_root_path) 10 | 11 | sh.caja(tinySelf_root_path) 12 | sh.caja(os.path.join(tinySelf_root_path, "tests")) 13 | sh.caja(os.path.join(tinySelf_root_path, "src/tinySelf/parser")) 14 | sh.caja(os.path.join(tinySelf_root_path, "src/tinySelf/vm")) 15 | # sh.mate_terminal(working_directory=tinySelf_root_path) 16 | --------------------------------------------------------------------------------