├── .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 | [](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 | '[1;32mok[0m\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 |
--------------------------------------------------------------------------------