├── .gitignore ├── .gitmodules ├── .karma.js ├── .travis.yml ├── README.rst ├── build.py ├── doc ├── build-html.sh ├── environment.yml └── source │ ├── conf.py │ └── index.rst ├── example ├── debugger.gif ├── hello.html ├── hello.py ├── pystone.html └── pystone.py ├── jaspy ├── __init__.py ├── cli.py ├── converter.py ├── debugger.py ├── event.py ├── interactive.py ├── metadata.py ├── preprocessor.py ├── pydev.py └── server.py ├── modules ├── _builtins.py ├── _thread.js ├── dom.js ├── greenlet.js └── time.js ├── notes └── README.rst ├── package.json ├── readthedocs.yml ├── requirements.txt ├── setup.py ├── spec ├── dict_spec.js ├── executor_spec.js ├── float_spec.js ├── javascripts │ └── support │ │ ├── function-bind.js │ │ └── jasmine.yml └── spec.js └── src ├── __init__.js ├── base.js ├── executor.js ├── language ├── __init__.js ├── parser.js └── tokenizer.js └── runtime ├── __init__.js ├── base.js ├── bridge.js ├── code.js ├── constants.js ├── core ├── __init__.js ├── bytes.js ├── cell.js ├── dict.js ├── exception.js ├── float.js ├── func.js ├── generator.js ├── int.js ├── iterator.js ├── list.js ├── method.js ├── property.js ├── slice.js ├── str.js ├── traceback.js ├── tuple.js └── wrapper.js ├── debugger.js ├── dis.js ├── execute.js ├── frame.js ├── future.js ├── module.js ├── object.js ├── python ├── __init__.js ├── bool.js ├── boot.js ├── builtins.js ├── bytes.js ├── dict.js ├── exception.js ├── float.js ├── function.js ├── generator.js ├── helpers.js ├── int.js ├── iterator.js ├── list.js ├── method.js ├── module.js ├── none.js ├── object.js ├── property.js ├── slice.js ├── str.js ├── tuple.js ├── type.js └── wrapper.js ├── sys.js ├── threading.js ├── type.js └── vm.js /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | playground 3 | .idea 4 | __pycache__ 5 | node_modules 6 | coverage 7 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libs/BigInteger.js"] 2 | path = libs/biginteger 3 | url = https://github.com/peterolson/BigInteger.js.git 4 | [submodule "libs/text-encoding"] 5 | path = libs/text-encoding 6 | url = https://github.com/inexorabletash/text-encoding 7 | [submodule "libs/siphash-js"] 8 | path = libs/siphash 9 | url = https://github.com/jedisct1/siphash-js.git 10 | [submodule "libs/unicode"] 11 | path = libs/unicode 12 | url = https://github.com/qsantos/unicode.js 13 | -------------------------------------------------------------------------------- /.karma.js: -------------------------------------------------------------------------------- 1 | module.exports = function (config) { 2 | config.set({ 3 | basePath: '', 4 | frameworks: ['jasmine'], 5 | files: [ 6 | 'spec/javascripts/support/function-bind.js', 7 | 'build/jaspy.js', 8 | 'spec/*.js' 9 | ], 10 | browsers: ['Firefox'], 11 | singleRun: true, 12 | reporters: ['progress', 'coverage'], 13 | preprocessors: {'build/jaspy.js': ['coverage']}, 14 | coverageReporter: { 15 | type: 'lcov', 16 | dir: 'coverage/', 17 | subdir: '.' 18 | }, 19 | browserNoActivityTimeout: 60000 20 | }); 21 | }; -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.5" 4 | before_install: 5 | - export DISPLAY=:99.0 6 | - sh -e /etc/init.d/xvfb start 7 | before_script: 8 | - nvm install 0.12 9 | - node --version 10 | - npm --version 11 | - nvm --version 12 | - npm install 13 | script: 14 | - npm build 15 | - npm test 16 | after_success: 17 | - cat ./coverage/lcov.info | ./node_modules/.bin/coveralls 18 | addons: 19 | firefox: latest -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Jaspy 2 | ===== 3 | 4 | |pypi| |build| |coverage| |docs| |gitter| 5 | 6 | Jaspy is a Python VM written entirely from scratch in JavaScript with some unique 7 | features. Jaspy supports multiple threads, comes with an integrated debugger which 8 | offers remote debugging and provides a flexible preprocessor based architecture. 9 | Speed is explicitly not a main goal of this project. Jaspy aims to illustrate how 10 | web programming on the client side could be done by exploring new ways. 11 | 12 | 13 | Features 14 | -------- 15 | - **suspendable** interpreter with support for **threading** and greenlets 16 | - integrated **debugger** and interactive remote debugging (CLI, PyCharm, …) 17 | - **flexible** preprocessor based architecture to optimize Jaspy for your needs 18 | - easily **extensible** with native JavaScript modules (time_, dom_, …) 19 | - full support for meta-classes, builtin subclassing and operator overloading 20 | - asynchronous imports and arbitrary-length integers based on BigInteger.js_ 21 | 22 | .. _BigInteger.js: https://github.com/peterolson/BigInteger.js 23 | .. _time: https://github.com/koehlma/jaspy/blob/master/modules/time.js 24 | .. _dom: https://github.com/koehlma/jaspy/blob/master/modules/dom.js 25 | 26 | Quickstart 27 | ---------- 28 | Jaspy comes with an integrated development server and interactive debugger! 29 | 30 | First install the dependencies, if they are not installed already: 31 | 32 | .. code:: sh 33 | 34 | pip3 install --user -r requirements.txt 35 | pip3 install --user ptpython pygments 36 | 37 | Clone the repository and build the interpreter: 38 | 39 | .. code:: sh 40 | 41 | git clone --recursive https://github.com/koehlma/jaspy.git; cd jaspy 42 | python3 build.py # build the interpreter 43 | 44 | Switch to the example directory and start the server in interactive mode: 45 | 46 | .. code:: sh 47 | 48 | cd example 49 | PYTHONPATH=../ python3 -m jaspy.cli --interactive 50 | 51 | Visit http://localhost:8080/hello.html in your browser and click run: 52 | 53 | .. image:: https://raw.githubusercontent.com/koehlma/jaspy/master/example/debugger.gif 54 | :alt: Jaspy Screencast 55 | :align: center 56 | 57 | 58 | Alternatives 59 | ------------ 60 | There are already many other Python-to-JavaScript approaches out there: 61 | 62 | - `Brython `_ 63 | - `PyPy.js `_ 64 | - `Skulpt `_ 65 | - `Batavia `_ 66 | - `Pyjs `_ 67 | - … 68 | 69 | Most of them are faster than Jaspy but none of them offers the unique features of 70 | Jaspy, which are the fully suspendable interpreter with threading support, the 71 | integrated debugger and the flexible, preprocessor based architecture. 72 | 73 | 74 | Speed 75 | ----- 76 | Just to get an impression how slow Jaspy really is! 77 | 78 | +-----------------------------------------------+------------------+ 79 | | | pystones/second | 80 | +===============================================+==================+ 81 | | Jaspy (enabled Debugger, enabled Threading) | 195 | 82 | +-----------------------------------------------+------------------+ 83 | | Jaspy (disabled Debugger, enabled Threading) | 199 | 84 | +-----------------------------------------------+------------------+ 85 | | Jaspy (disabled Debugger, disabled Threading) | 206 | 86 | +-----------------------------------------------+------------------+ 87 | | Brython | 4184 | 88 | +-----------------------------------------------+------------------+ 89 | | PyPy.js (cold) | 41425 | 90 | +-----------------------------------------------+------------------+ 91 | | PyPy.js (warm) | 847457 | 92 | +-----------------------------------------------+------------------+ 93 | 94 | However this is a somewhat unfair benchmark because no dom manipulation or anything 95 | else browser specific is going on. Surprisingly threading and debugging introduces nearly 96 | no overhead. 97 | 98 | 99 | State 100 | ----- 101 | This project is still in an alpha state. The APIs are unstable, it is untested and not 102 | ready for productive use. Some of the features listed above are not yet implemented. 103 | 104 | I started this project in my semester break and now, as the new semester started, I have 105 | much less spare time. Therefore it might take a while until I will be able to invest much 106 | more time into it. However I very welcome all sorts of contributions. 107 | 108 | 109 | Contributions 110 | ------------- 111 | If you like the ideas of Jaspy feel free to join, there are many things to do: 112 | 113 | - implement all the batteries-included-builtin stuff of Python 114 | - implement native JS modules for the DOM, JS objects and some Web APIs 115 | - improve the debugger and make it fully compatible to the PyDev protocol 116 | - implement a parser and bytecode compiler in JavaScript 117 | - complete and adjust the implementation of the Python bytecode VM 118 | - support for Apache Cordova (Jaspy for cross platform mobile applications) 119 | - implement a neat UI library on top of Jaspy (using a flexbox based grid) 120 | - implement a just-in-time compiler to speed things up 121 | - add a virtual file system (consider using: `BrowserFS `_) 122 | - … and, of course, your own great ideas and cool features 123 | 124 | Do not hesitate to contribute or ask if there is anything unclear about the code or the 125 | process of contributing in general. 126 | 127 | 128 | Structure 129 | --------- 130 | 131 | :libs: third-party dependencies 132 | :modules: bundled native JavaScript modules 133 | :src: JavaScript source files (need to be preprocessed) 134 | :jaspy: Python server, converter and remote debugger 135 | 136 | 137 | Credits 138 | ------- 139 | Many thanks to the `Brython `_ project for the inspiration for 140 | many parts of code of the builtin-classes. Many thanks also to the book `“500 Lines or 141 | Less”`_ which is a good starting point if you want to know how the interpreter works. 142 | 143 | .. _`“500 Lines or Less”`: http://aosabook.org/en/500L/a-python-interpreter-written-in-python.html 144 | 145 | 146 | .. |pypi| image:: https://img.shields.io/pypi/v/jaspy.svg?style=flat-square&label=latest%20version 147 | :target: https://pypi.python.org/pypi/jaspy 148 | 149 | .. |build| image:: https://img.shields.io/travis/koehlma/jaspy/master.svg?style=flat-square&label=build 150 | :target: https://travis-ci.org/koehlma/jaspy 151 | 152 | .. |docs| image:: https://readthedocs.org/projects/jaspy/badge/?version=latest&style=flat-square 153 | :target: https://jaspy.readthedocs.org/en/latest/ 154 | 155 | .. |coverage| image:: https://img.shields.io/coveralls/koehlma/jaspy/master.svg?style=flat-square 156 | :target: https://coveralls.io/github/koehlma/jaspy?branch=master 157 | 158 | .. |gitter| image:: https://img.shields.io/badge/gitter-join%20chat-1dce73.svg?style=flat-square 159 | :target: https://gitter.im/koehlma/jaspy 160 | -------------------------------------------------------------------------------- /build.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | import argparse 17 | import os 18 | 19 | from jaspy import metadata 20 | from jaspy.converter import convert 21 | from jaspy.preprocessor import process 22 | 23 | 24 | __path__ = os.path.dirname(__file__) 25 | if __path__: 26 | os.chdir(__path__) 27 | 28 | 29 | with open(os.path.join(__path__, 'modules', '_builtins.py')) as builtins: 30 | code = compile(builtins.read(), '', 'exec') 31 | source = convert(code) 32 | builtins_source = 'jaspy.module(%r, %s);' % ('_builtins', source) 33 | 34 | 35 | if __name__ == '__main__': 36 | parser = argparse.ArgumentParser() 37 | 38 | parser.add_argument('--debug', action='store_true', default=False) 39 | parser.add_argument('--debug-instructions', action='store_true', default=False) 40 | # TODO: disable DEBUG_EXCEPTIONS when production ready 41 | parser.add_argument('--debug-exceptions', action='store_true', default=True) 42 | parser.add_argument('--debug-threading', action='store_true', default=False) 43 | 44 | parser.add_argument('--exclude-bigint', action='store_true', default=False) 45 | parser.add_argument('--exclude-siphash', action='store_true', default=False) 46 | 47 | parser.add_argument('--include-encoding', action='store_true', default=False) 48 | 49 | parser.add_argument('--disable-debugger', action='store_true', default=False) 50 | parser.add_argument('--disable-threading', action='store_true', default=False) 51 | 52 | parser.add_argument('--threading-limit', type=int, default=5000) 53 | 54 | parser.add_argument('--modules', nargs='*') 55 | 56 | arguments = parser.parse_args() 57 | 58 | libs = [] 59 | if not arguments.exclude_bigint: 60 | libs.append('biginteger/BigInteger.js') 61 | if not arguments.exclude_siphash: 62 | libs.append('siphash/lib/siphash.js') 63 | if arguments.include_encoding: 64 | libs.append('text-encoding/lib/encoding.js') 65 | 66 | namespace = { 67 | 'DEBUG': arguments.debug, 68 | 'DEBUG_INSTRUCTIONS': arguments.debug_instructions, 69 | 'DEBUG_EXCEPTIONS': arguments.debug_exceptions, 70 | 'DEBUG_THREADING': arguments.debug_threading, 71 | 72 | 'ENABLE_DEBUGGER': not arguments.disable_debugger, 73 | 'ENABLE_THREADING': not arguments.disable_threading, 74 | 75 | 'ENABLE_ASSERTIONS': True, 76 | 77 | 'THREADING_LIMIT': arguments.threading_limit, 78 | 79 | 'UNICODE_SUPPORT': True, 80 | 81 | 'modules': arguments.modules or [], 82 | 83 | 'metadata': metadata, 84 | 85 | 'libs': libs, 86 | 87 | '_builtins': builtins_source 88 | } 89 | 90 | if not os.path.exists('build'): 91 | os.mkdir('build') 92 | 93 | with open('build/jaspy.js', 'w') as output: 94 | output.write(process('src/__init__.js', namespace)) 95 | 96 | -------------------------------------------------------------------------------- /doc/build-html.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | 3 | sphinx-build -b html source build/html -------------------------------------------------------------------------------- /doc/environment.yml: -------------------------------------------------------------------------------- 1 | name: py35 2 | dependencies: 3 | - openssl=1.0.2g=0 4 | - pip=8.1.1=py35_0 5 | - python=3.5.1=0 6 | - readline=6.2=2 7 | - setuptools=20.3=py35_0 8 | - sqlite=3.9.2=0 9 | - tk=8.5.18=0 10 | - wheel=0.29.0=py35_0 11 | - xz=5.0.5=1 12 | - zlib=1.2.8=0 13 | - pip: 14 | - aiohttp>=0.21.5 -------------------------------------------------------------------------------- /doc/source/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | # Copyright (C) 2016, Maximilian Köhl 5 | # 6 | # This program is free software: you can redistribute it and/or modify it under 7 | # the terms of the GNU Lesser General Public License version 3 as published by 8 | # the Free Software Foundation. 9 | # 10 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 11 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 12 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Lesser General Public License along 15 | # with this program. If not, see . 16 | 17 | import os 18 | import sys 19 | 20 | __dir__ = os.path.dirname(__file__) 21 | sys.path.insert(0, os.path.join(__dir__, '..', '..')) 22 | 23 | from jaspy import metadata 24 | 25 | extensions = [ 26 | 'sphinx.ext.autodoc', 27 | 'sphinx.ext.doctest', 28 | 'sphinx.ext.intersphinx', 29 | 'sphinx.ext.todo', 30 | 'sphinx.ext.mathjax', 31 | 'sphinx.ext.viewcode', 32 | ] 33 | 34 | templates_path = ['_templates'] 35 | 36 | source_suffix = '.rst' 37 | 38 | master_doc = 'index' 39 | 40 | project = 'Jaspy' 41 | copyright = '2016, Maximilian Köhl' 42 | author = 'Maximilian Köhl' 43 | 44 | version = metadata.__version__ 45 | release = metadata.__version__ 46 | 47 | language = None 48 | 49 | exclude_patterns = [] 50 | 51 | pygments_style = 'sphinx' 52 | 53 | todo_include_todos = False 54 | 55 | html_static_path = ['_static'] 56 | 57 | intersphinx_mapping = {'python': ('https://docs.python.org/3.5', None)} 58 | -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | .. currentmodule:: jaspy 2 | 3 | Jaspy Documentation 4 | =================== 5 | 6 | 7 | Indices and tables 8 | ================== 9 | 10 | * :ref:`genindex` 11 | * :ref:`modindex` 12 | * :ref:`search` 13 | 14 | -------------------------------------------------------------------------------- /example/debugger.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koehlma/jaspy/585c11d135f565c8a19f34f9e2fb6eeb22328405/example/debugger.gif -------------------------------------------------------------------------------- /example/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Jaspy — Hello! 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /example/hello.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | import sys 17 | import time 18 | 19 | import _thread 20 | 21 | import dom 22 | 23 | 24 | # command line arguments 25 | print('command line arguments:') 26 | print(sys.argv) 27 | 28 | print('Hello ', sys.argv[1]) 29 | 30 | 31 | for x in ['abc', 1, 2, True, None]: 32 | print(x) 33 | 34 | 35 | # dom manipulation 36 | p = dom.Element('p') 37 | p.text = 'Hello ' + sys.argv[1] 38 | p.css('background', 'black') 39 | p.css('color', 'white') 40 | 41 | dom.get_body().append(p) 42 | 43 | 44 | # exceptions 45 | def recursion(level=10): 46 | if level < 0: 47 | raise Exception('example exception') 48 | else: 49 | recursion(level - 1) 50 | 51 | try: 52 | recursion() 53 | except Exception: 54 | print('exception caught!') 55 | 56 | 57 | # event listeners 58 | def on_click(element): 59 | print('click on element', element) 60 | 61 | 62 | button = dom.Element('button') 63 | button.text = 'Click Me!' 64 | button.register_listener('click', on_click) 65 | button.css('background', 'white') 66 | button.css('color', 'black') 67 | 68 | dom.get_body().append(button) 69 | 70 | print(hash('abc')) 71 | 72 | print(any([0, False, None])) 73 | 74 | for x, y in zip([1, 2, 3], ('a', 'b', 'c')): 75 | print('zip', x, y) 76 | 77 | print(sum([1, 2, 4])) 78 | print([item for item in enumerate(('a', 'b', 'c'))]) 79 | 80 | example_dict = { 81 | '123': 'abc', 82 | 123: 'xyz' 83 | } 84 | 85 | if example_dict['123'] == 'abc' and example_dict[123] == 'xyz': 86 | for key, value in example_dict.items(): 87 | print(key, value) 88 | if 123 in example_dict and True not in example_dict: 89 | del example_dict['123'] 90 | try: 91 | print('Error:', example_dict['123']) 92 | except KeyError: 93 | print('Dictionaries are working!') 94 | 95 | 96 | # multiple threads 97 | def button_animation(): 98 | state = True 99 | while True: 100 | # this also works without time sleep because jaspy provides preemptive 101 | # multitasking but it would produce an enormous load 102 | time.sleep(0.5) 103 | if state: 104 | button.css('background', 'black') 105 | button.css('color', 'white') 106 | state = False 107 | else: 108 | button.css('background', 'white') 109 | button.css('color', 'black') 110 | state = True 111 | 112 | 113 | thread = _thread.start_new_thread(button_animation) 114 | -------------------------------------------------------------------------------- /example/pystone.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Jaspy — PyStone 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /jaspy/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | from .server import Server 17 | -------------------------------------------------------------------------------- /jaspy/converter.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | import argparse 17 | import dis 18 | import os.path 19 | import types 20 | 21 | 22 | def convert(const): 23 | js = [] 24 | if const is None or isinstance(const, bool): 25 | js.append('jaspy.%r' % const) 26 | elif isinstance(const, int): 27 | js.append('jaspy.Int.pack(%r)' % const) 28 | elif isinstance(const, float): 29 | js.append('jaspy.Float.pack(%r)' % const) 30 | elif isinstance(const, str): 31 | js.append('jaspy.Str.pack(%r)' % const) 32 | elif isinstance(const, bytes): 33 | js.append('jaspy.pack_bytes(%r)' % ', '.join(map(int, const))) 34 | elif isinstance(const, tuple): 35 | js.append('jaspy.pack_tuple([%s])' % ', '.join(map(convert, const))) 36 | elif isinstance(const, types.CodeType): 37 | js.append('(new jaspy.PythonCode(%s, {' % repr(const.co_code)[1:]) 38 | js.append('name: %r,' % const.co_name) 39 | js.append('filename: %r,' % const.co_filename) 40 | js.append('constants: [%s],' % ', '.join(map(convert, const.co_consts))) 41 | js.append('flags: %r,' % const.co_flags) 42 | js.append('names: [%s],' % ', '.join(map(repr, const.co_names))) 43 | js.append('stacksize: %r,' % const.co_stacksize) 44 | js.append('argcount: %r,' % const.co_argcount) 45 | js.append('kwargcount: %r,' % const.co_kwonlyargcount) 46 | js.append('varnames: [%s],' % ', '.join(map(repr, const.co_varnames))) 47 | js.append('freevars: [%s],' % ', '.join(map(repr, const.co_freevars))) 48 | js.append('cellvars: [%s],' % ', '.join(map(repr, const.co_cellvars))) 49 | js.append('firstline: %r,' % const.co_firstlineno) 50 | js.append('lnotab: \'%s\'' % repr(const.co_lnotab)[2:-1]) 51 | js.append('}))') 52 | else: 53 | raise TypeError('invalid type of constant', const) 54 | return '(' + ''.join(js) + ')' 55 | 56 | 57 | def disassemble(const): 58 | dis.dis(const) 59 | for const in const.co_consts: 60 | if isinstance(const, types.CodeType): 61 | disassemble(const) 62 | 63 | 64 | def compile_and_convert(filename): 65 | with open(filename, 'r') as source_file: 66 | source = source_file.read() 67 | code = compile(source, filename, 'exec') 68 | return convert(code) 69 | 70 | 71 | def main(): 72 | parser = argparse.ArgumentParser() 73 | parser.add_argument('module', type=argparse.FileType('r')) 74 | parser.add_argument('--debug', default=False, action='store_true') 75 | 76 | arguments = parser.parse_args() 77 | 78 | filename = os.path.basename(arguments.module.name) 79 | code = compile(arguments.module.read(), filename, 'exec') 80 | source = convert(code) 81 | name = os.path.basename(arguments.module.name).partition('.')[0] 82 | 83 | with open(arguments.module.name + '.js', 'w') as output: 84 | output.write('jaspy.module(%r, %s);' % (name, source)) 85 | 86 | if arguments.debug: 87 | disassemble(code) 88 | 89 | 90 | if __name__ == '__main__': 91 | main() 92 | -------------------------------------------------------------------------------- /jaspy/debugger.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | import asyncio 17 | import enum 18 | import itertools 19 | import json 20 | 21 | import aiohttp 22 | 23 | from . import converter 24 | from .event import Event 25 | 26 | 27 | class Commands(enum.Enum): 28 | RUN = 'run' 29 | 30 | THREAD_SUSPEND = 'suspend_thread' 31 | THREAD_RESUME = 'resume_thread' 32 | THREAD_KILL = 'kill_thread' 33 | 34 | STEP_OVER = 'step_over' 35 | STEP_INTO = 'step_into' 36 | STEP_OUT = 'step_out' 37 | 38 | GET_THREADS = 'get_threads' 39 | GET_LOCALS = 'get_locals' 40 | 41 | ADD_BREAK = 'add_break' 42 | REMOVE_BREAK = 'remove_break' 43 | 44 | RUN_IN_FRAME = 'run_in_frame' 45 | RUN_IN_THREAD = 'run_in_thread' 46 | 47 | ADD_EXCEPTION_BREAK = 'add_exception_break' 48 | REMOVE_EXCEPTION_BREAK = 'remove_exception_break' 49 | 50 | JS_EVAL = 'js_eval' 51 | JS_DEBUGGER = 'js_debugger' 52 | 53 | def make(self, sequence_number, *arguments): 54 | return json.dumps({'cmd': self.value, 'seq': sequence_number, 'args': arguments}) 55 | 56 | 57 | class Events(enum.Enum): 58 | HELLO = 'hello' 59 | 60 | SUCCESS = 'success' 61 | ERROR = 'error' 62 | 63 | RUNNING = 'running' 64 | 65 | THREAD_CREATED = 'thread_created' 66 | THREAD_SUSPENDED = 'thread_suspended' 67 | THREAD_RESUMED = 'thread_resumed' 68 | THREAD_FINISHED = 'thread_finished' 69 | 70 | BREAK_ADDED = 'break_added' 71 | BREAK_REMOVED = 'break_removed' 72 | 73 | EXCEPTION_BREAK_ADDED = 'exception_break_added' 74 | EXCEPTION_BREAK_REMOVED = 'exception_break_removed' 75 | 76 | THREADS = 'threads' 77 | LOCALS = 'locals' 78 | 79 | CONSOLE_LOG = 'console_log' 80 | CONSOLE_ERROR = 'console_error' 81 | 82 | 83 | sessions = [] 84 | 85 | session_created = Event() 86 | session_closed = Event() 87 | 88 | 89 | class Debugger: 90 | def __init__(self, websocket): 91 | self.websocket = websocket 92 | 93 | self.on_hello = Event() 94 | 95 | self.on_success = Event() 96 | self.on_error = Event() 97 | 98 | self.on_running = Event() 99 | 100 | self.on_thread_created = Event() 101 | self.on_thread_suspended = Event() 102 | self.on_thread_resumed = Event() 103 | self.on_thread_finished = Event() 104 | 105 | self.on_break_added = Event() 106 | self.on_break_removed = Event() 107 | 108 | self.on_exception_break_added = Event() 109 | self.on_exception_break_removed = Event() 110 | 111 | self.on_threads = Event() 112 | self.on_locals = Event() 113 | 114 | self.on_console_log = Event() 115 | self.on_console_error = Event() 116 | 117 | self.events = { 118 | Events.HELLO: self.on_hello, 119 | 120 | Events.SUCCESS: self.on_success, 121 | Events.ERROR: self.on_error, 122 | 123 | Events.RUNNING: self.on_running, 124 | 125 | Events.THREAD_CREATED: self.on_thread_created, 126 | Events.THREAD_SUSPENDED: self.on_thread_suspended, 127 | Events.THREAD_RESUMED: self.on_thread_resumed, 128 | Events.THREAD_FINISHED: self.on_thread_finished, 129 | 130 | Events.BREAK_ADDED: self.on_break_added, 131 | Events.BREAK_REMOVED: self.on_break_removed, 132 | 133 | Events.EXCEPTION_BREAK_ADDED: self.on_exception_break_added, 134 | Events.EXCEPTION_BREAK_REMOVED: self.on_exception_break_added, 135 | 136 | Events.THREADS: self.on_threads, 137 | Events.LOCALS: self.on_locals, 138 | 139 | Events.CONSOLE_LOG: self.on_console_log, 140 | Events.CONSOLE_ERROR: self.on_console_error 141 | } 142 | 143 | self.pending = {} 144 | 145 | self.sequence_counter = itertools.count(1, 2) 146 | 147 | def send(self, command, *arguments, sequence_number=None): 148 | if sequence_number is None: 149 | sequence_number = next(self.sequence_counter) 150 | self.websocket.send_str(command.make(sequence_number, *arguments)) 151 | return sequence_number 152 | 153 | def run(self): 154 | self.send(Commands.RUN) 155 | 156 | def suspend_thread(self, thread_id): 157 | self.send(Commands.THREAD_SUSPEND, thread_id) 158 | 159 | def resume_thread(self, thread_id): 160 | self.send(Commands.THREAD_RESUME, thread_id) 161 | 162 | def kill_thread(self, thread_id): 163 | self.send(Commands.THREAD_KILL, thread_id) 164 | 165 | def step_over(self, thread_id): 166 | self.send(Commands.STEP_OVER, thread_id) 167 | 168 | def step_into(self, thread_id): 169 | self.send(Commands.STEP_INTO, thread_id) 170 | 171 | def step_out(self, thread_id): 172 | self.send(Commands.STEP_OUT, thread_id) 173 | 174 | def get_threads(self): 175 | self.send(Commands.GET_THREADS) 176 | 177 | def add_break(self, filename, line, condition=None, expression=None): 178 | self.send(Commands.ADD_BREAK, filename, line, condition, expression) 179 | 180 | def remove_break(self, filename, line): 181 | self.send(Commands.REMOVE_BREAK, filename, line) 182 | 183 | def add_exception_break(self, name, on_termination=False, on_raise=True): 184 | self.send(Commands.ADD_EXCEPTION_BREAK, name, on_termination, on_raise) 185 | 186 | def remove_exception_break(self, name): 187 | self.send(Commands.REMOVE_EXCEPTION_BREAK, name) 188 | 189 | def js_eval(self, source): 190 | self.send(Commands.JS_EVAL, source) 191 | 192 | def js_debugger(self): 193 | self.send(Commands.JS_DEBUGGER) 194 | 195 | def exec_in_frame(self, thread_id, frame_id, source): 196 | code = converter.convert(compile(source, '', 'exec')) 197 | return self.send(Commands.RUN_IN_FRAME, thread_id, frame_id, code) 198 | 199 | def eval_in_frame(self, thread_id, frame_id, source): 200 | code = converter.convert(compile(source, '', 'eval')) 201 | return self.send(Commands.RUN_IN_FRAME, thread_id, frame_id, code) 202 | 203 | def run_in_thread(self, source): 204 | code = converter.convert(compile(source, '', 'exec')) 205 | return self.send(Commands.RUN_IN_THREAD, code) 206 | 207 | def get_locals(self, thread_id, frame_id): 208 | seq = self.send(Commands.GET_LOCALS, thread_id, frame_id) 209 | self.pending[seq] = Event() 210 | return self.pending[seq] 211 | 212 | async def debug(self): 213 | sessions.append(self) 214 | try: 215 | session_created.emit(self) 216 | async for message in self.websocket: 217 | if message.tp == aiohttp.MsgType.text: 218 | data = json.loads(message.data) 219 | seq = int(data['seq']) 220 | if seq in self.pending: 221 | self.pending[seq].emit(data['event'], data['args']) 222 | del self.pending[seq] 223 | event = self.events[Events(data['event'])] 224 | event.emit(self, seq, *data['args']) 225 | finally: 226 | sessions.remove(self) 227 | session_closed.emit(self) 228 | -------------------------------------------------------------------------------- /jaspy/event.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | 17 | class Event: 18 | def __init__(self): 19 | self.listeners = [] 20 | 21 | def __iadd__(self, listener): 22 | self.listeners.append(listener) 23 | return self 24 | 25 | def __isub__(self, listener): 26 | self.listeners.remove(listener) 27 | return self 28 | 29 | def emit(self, *arguments, **keywords): 30 | for listener in self.listeners: 31 | listener(*arguments, **keywords) 32 | -------------------------------------------------------------------------------- /jaspy/interactive.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | import asyncio 17 | import os 18 | 19 | from pygments.token import Token 20 | 21 | from prompt_toolkit.styles import style_from_dict 22 | from prompt_toolkit.shortcuts import print_tokens 23 | 24 | from ptpython.repl import embed 25 | 26 | from . import debugger 27 | 28 | 29 | INFO = Token.Info 30 | SUCCESS = Token.Success 31 | WARNING = Token.Warning 32 | ERROR = Token.Error 33 | 34 | style = style_from_dict({ 35 | Token.Info: '#5555FF', 36 | Token.Success: '#00FF00', 37 | Token.Warning: '#FF5500', 38 | Token.Error: '#FF0000', 39 | Token.Trace: '#888888', 40 | Token.Variable: '#FF8888' 41 | }, include_defaults=True) 42 | 43 | 44 | FRAME_TEMPLATE = ('| Frame {} [{name}]:{linesep}' 45 | '| File: {file}{linesep}' 46 | '| Line: {line}{linesep}') 47 | 48 | VARIABLE_HEADER = 'Received variable {} from thread {} frame {}!' 49 | 50 | 51 | def format_variable_path(path): 52 | if not path: 53 | return '' 54 | parts = [] 55 | for component in path: 56 | if isinstance(component, str): 57 | if parts: parts.append('.') 58 | parts.append(component) 59 | else: 60 | parts.append('[' + str(component) + ']') 61 | return ''.join(parts) 62 | 63 | 64 | class InteractiveConsole: 65 | def __init__(self, server, history=os.path.expanduser('~/.jaspy.history')): 66 | self.server = server 67 | self.namespace = {'sessions': debugger.sessions} 68 | self.history = history 69 | self.cli = None 70 | self.running = False 71 | 72 | debugger.session_created += self.on_session_created 73 | debugger.session_closed += self.on_session_closed 74 | 75 | self.server.on_running += self.on_server_running 76 | 77 | def start(self): 78 | self.running = True 79 | 80 | coroutine = embed(self.namespace, self.namespace, title='Jaspy Console', 81 | patch_stdout=True, history_filename=self.history, 82 | return_asyncio_coroutine=True) 83 | 84 | # HACK: nasty hack to gain access to the command line interface wrapper 85 | self.cli = coroutine.gi_frame.f_locals['cli'] 86 | 87 | future = asyncio.ensure_future(coroutine) 88 | future.add_done_callback(self.on_closed) 89 | return future 90 | 91 | def print_tokens(self, tokens): 92 | if self.running: 93 | def printer(): 94 | print_tokens(tokens, style=style) 95 | self.cli.run_in_terminal(printer) 96 | 97 | def print_message(self, message, kind=SUCCESS): 98 | self.print_tokens([(kind, '<<< ' + message + os.linesep)]) 99 | 100 | def on_closed(self, future): 101 | self.running = False 102 | 103 | def on_server_running(self, server): 104 | msg = 'Sever is running on http://{}:{}/!'.format(server.host, server.port) 105 | self.print_message(msg, INFO) 106 | 107 | def on_session_created(self, session): 108 | self.print_message('Remote debugging session created!') 109 | self.print_message('debugger = {}'.format(session), INFO) 110 | 111 | self.namespace['debugger'] = session 112 | 113 | session.on_error += self.on_error 114 | session.on_success += self.on_success 115 | 116 | session.on_running += self.on_running 117 | 118 | session.on_thread_suspended += self.on_thread_suspended 119 | 120 | session.on_locals += self.on_locals 121 | 122 | session.on_console_log += self.on_console_log 123 | session.on_console_error += self.on_console_error 124 | 125 | def on_session_closed(self, session): 126 | self.print_message('Remote debugging session closed!', WARNING) 127 | 128 | def on_thread_suspended(self, session, seq, thread_id, frames): 129 | self.print_message('Thread {} has been suspended!'.format(thread_id)) 130 | tokens = [(Token.Trace, '| Traceback (most recent call first):' + os.linesep)] 131 | self.print_tokens(tokens) 132 | for index, frame in enumerate(frames): 133 | message = FRAME_TEMPLATE.format(index, linesep=os.linesep, **frame) 134 | self.print_tokens([(Token.Trace, message)]) 135 | 136 | def on_error(self, session, seq, message): 137 | self.print_message('Error: ' + message, ERROR) 138 | 139 | def on_success(self, session, seq, message): 140 | self.print_message('Success: ' + message, SUCCESS) 141 | 142 | def on_running(self, session, seq, module_name, argv): 143 | self.print_message('Program is running!') 144 | 145 | def on_variable(self, session, seq, thread_id, frame_id, path, result): 146 | header = VARIABLE_HEADER.format(format_variable_path(path), thread_id, frame_id) 147 | self.print_message(header) 148 | tokens = [] 149 | for name in sorted(result.keys()): 150 | info = result[name] 151 | tokens.extend([ 152 | (Token.Trace, '| '), 153 | (Token.Variable, name), 154 | (Token.Normal, ' = '), 155 | (Token.Trace, '{%s} ' % info['type']), 156 | (Token.Normal, info['value'] + os.linesep) 157 | ]) 158 | self.print_tokens(tokens) 159 | 160 | def on_console_log(self, session, seq, *strings): 161 | self.print_tokens([(Token.Trace, ' '.join(strings) + os.linesep)]) 162 | 163 | def on_console_error(self, session, seq, *strings): 164 | self.print_tokens([(Token.Warning, ' '.join(strings) + os.linesep)]) 165 | 166 | def on_locals(self, session, seq, result): 167 | self.print_message('Received local frame variables!') 168 | tokens = [] 169 | for name in sorted(result.keys()): 170 | info = result[name] 171 | tokens.extend([ 172 | (Token.Trace, '| '), 173 | (Token.Variable, name), 174 | (Token.Normal, ' = '), 175 | (Token.Trace, '{%s} ' % info['type']), 176 | (Token.Normal, info['value'] + os.linesep) 177 | ]) 178 | self.print_tokens(tokens) 179 | 180 | 181 | -------------------------------------------------------------------------------- /jaspy/metadata.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | __version_info__ = (0, 1, 0, 'dev0', 0) 17 | 18 | __version__ = '0.1.0.dev0' 19 | __project__ = 'Jaspy Python Interpreter' 20 | __author__ = 'Maximilian Köhl' 21 | __email__ = 'mail@koehlma.de' 22 | -------------------------------------------------------------------------------- /jaspy/server.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | import os.path 17 | 18 | import aiohttp 19 | import aiohttp.web 20 | 21 | from .event import Event 22 | from .debugger import Debugger 23 | from .converter import compile_and_convert 24 | 25 | 26 | class Server: 27 | def __init__(self, jaspy_js, modules_dir, host='localhost', port=8080, root_dir='.'): 28 | self.jaspy_js = jaspy_js 29 | self.modules_dir = modules_dir 30 | 31 | self.host = host 32 | self.port = port 33 | 34 | self.root_dir = root_dir 35 | 36 | self.application = aiohttp.web.Application() 37 | self.application.on_shutdown.append(self.on_shutdown) 38 | 39 | self.router = self.application.router 40 | self.router.add_route('GET', '/debugger', self.debugger) 41 | self.router.add_route('GET', '/load/{name}', self.load) 42 | self.router.add_route('GET', '/jaspy.js', self.jaspy) 43 | self.router.add_static('/modules', self.modules_dir) 44 | self.router.add_static('/', self.root_dir) 45 | 46 | self.on_running = Event() 47 | 48 | self.connections = [] 49 | 50 | async def on_shutdown(self, application): 51 | for websocket in self.connections: 52 | await websocket.close(message='Server Shutdown') 53 | 54 | async def debugger(self, request): 55 | websocket = aiohttp.web.WebSocketResponse() 56 | self.connections.append(websocket) 57 | try: 58 | await websocket.prepare(request) 59 | debugger = Debugger(websocket) 60 | await debugger.debug() 61 | finally: 62 | self.connections.remove(websocket) 63 | return websocket 64 | 65 | async def load(self, request): 66 | name = request.match_info['name'] 67 | url = os.path.join(self.root_dir, '/'.join(name.split('.'))) 68 | if os.path.isdir(url): 69 | url = os.path.join(url, '__init__.py') 70 | else: 71 | url = '{}.py'.format(url) 72 | source = compile_and_convert(url) 73 | text = 'jaspy.module(%r, %s);' % (name, source) 74 | return aiohttp.web.Response(text=text, content_type='application/javascript') 75 | 76 | def jaspy(self, request): 77 | with open(self.jaspy_js, 'r') as jaspy_js_file: 78 | text = jaspy_js_file.read() 79 | return aiohttp.web.Response(text=text, content_type='application/javascript') 80 | 81 | def run(self): 82 | aiohttp.web.run_app(self.application, host=self.host, port=self.port, 83 | shutdown_timeout=5, 84 | print=lambda *_: self.on_running.emit(self)) 85 | 86 | def shutdown(self): 87 | self.application.shutdown() 88 | -------------------------------------------------------------------------------- /modules/_builtins.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | """ 17 | Implementation of non-trivial Python builtin functions. 18 | 19 | https://docs.python.org/3/library/functions.html 20 | """ 21 | 22 | 23 | NULL = object() 24 | 25 | 26 | def all(iterable): 27 | for element in iterable: 28 | if not element: 29 | return False 30 | return True 31 | 32 | 33 | def any(iterable): 34 | for element in iterable: 35 | if element: 36 | return True 37 | return False 38 | 39 | 40 | def next(iterator, default=NULL): 41 | if default is NULL: 42 | return iterator.__next__() 43 | else: 44 | try: 45 | return iterator.__next__() 46 | except StopIteration: 47 | return default 48 | 49 | 50 | def _counter(start=0): 51 | while True: 52 | yield start 53 | start += 1 54 | 55 | 56 | def enumerate(iterable, start=0): 57 | return zip(_counter(start), iterable) 58 | 59 | 60 | def filter(function, iterable): 61 | if function is None: 62 | return (item for item in iterable if item) 63 | else: 64 | return (item for item in iterable if function(item)) 65 | 66 | 67 | def map(function, *iterables): 68 | return (function(*args) for args in zip(*iterables)) 69 | 70 | 71 | def pow(x, y, z=None): 72 | return x ** y if z is None else (x ** y) % z 73 | 74 | 75 | def sum(iterable, start=0): 76 | for item in iterable: 77 | start += item 78 | return start 79 | 80 | 81 | def zip(*iterables): 82 | iterators = [iter(iterable) for iterable in iterables] 83 | while iterators: 84 | result = [] 85 | for iterator in iterators: 86 | try: 87 | result.append(iterator.__next__()) 88 | except StopIteration: 89 | return 90 | yield tuple(result) 91 | -------------------------------------------------------------------------------- /modules/_thread.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | jaspy.module('_thread', function ($, module) { 17 | module.$def('start_new_thread', function (target, args, kwargs) { 18 | var frame = $.vm.frame; 19 | var new_frame = $.call(target, args, kwargs); 20 | new_frame.thread = new $.Thread(new_frame); 21 | new_frame.back = null; 22 | new_frame.thread.enqueue(); 23 | $.vm.frame = frame; 24 | }, ['target', '*args', '**kwargs']); 25 | 26 | 27 | var LockType = module.$class('LockType'); 28 | 29 | LockType.$def('__exit__', function (self, exc_type, exc_value, exc_tb) { 30 | LockType.check(self); 31 | self.lock.release(); 32 | return $.False; 33 | }, ['exc_type', 'exc_value', 'exc_tb']); 34 | 35 | LockType.$def('acquire', function (self, state, frame) { 36 | LockType.check(self); 37 | switch (state) { 38 | case 0: 39 | if (self.lock.acquire()) { 40 | return $.True; 41 | } else { 42 | $.threading.drop(); 43 | return 1; 44 | } 45 | case 1: 46 | return $.True; 47 | } 48 | }); 49 | 50 | LockType.$def('release', function (self) { 51 | LockType.check(self); 52 | self.lock.release(); 53 | }); 54 | 55 | LockType.$def('locked', function (self) { 56 | LockType.check(self); 57 | return self.lock.locked() ? $.True : $.False; 58 | }); 59 | 60 | LockType.$def_alias('acquire', '__enter__'); 61 | 62 | 63 | 64 | 65 | 66 | module.$def('allocate_lock', function () { 67 | var lock = LockType.make(); 68 | lock.lock = new $.Lock(); 69 | return lock; 70 | }); 71 | 72 | 73 | 74 | }); 75 | -------------------------------------------------------------------------------- /modules/dom.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | jaspy.module('dom', function ($, module, builtins) { 17 | var MetaElement = module.$class('MetaElement', [builtins.type]); 18 | var Element = module.$class('Element', [builtins.object], MetaElement); 19 | 20 | 21 | Element.$def('__new__', function (cls, tag) { 22 | Element.check_subclass(cls); 23 | var self = new $.PyObject(cls); 24 | self.element = document.createElement($.Str.unpack(tag, 'div')); 25 | self.element.__element__ = self; 26 | return self; 27 | }, ['tag'], {defaults: {'tag': builtins.None}}); 28 | 29 | Element.$def('__str__', function (self) { 30 | Element.check(self); 31 | return $.Str.pack('<' + self.element.nodeName.toLowerCase() + ' element at 0x' + self.get_address() + '>'); 32 | }); 33 | 34 | Element.$def('__getitem__', function (self, name) { 35 | Element.check(self); 36 | return $.Str.pack(self.element.getAttribute($.Str.unpack(name))); 37 | }, ['name']); 38 | 39 | Element.$def('__setitem__', function (self, name, value) { 40 | Element.check(self); 41 | self.element.setAttribute($.Str.unpack(name), $.Str.unpack(value)); 42 | }, ['name', 'value']); 43 | 44 | Element.$def('__getattr__', function (self, name) { 45 | Element.check(self); 46 | var child = new $.PyObject(Element); 47 | child.element = document.createElement($.Str.unpack(name, 'div')); 48 | child.element.__element__ = self; 49 | self.element.appendChild(child.element); 50 | return child; 51 | }, ['name']); 52 | 53 | Element.$def_property('text', function (self) { 54 | Element.check(self); 55 | return $.Str.pack(self.element.textContent); 56 | }, function (self, value) { 57 | Element.check(self); 58 | self.element.textContent = $.Str.unpack(value); 59 | }); 60 | 61 | Element.$def_property('html', function (self) { 62 | Element.check(self); 63 | return $.Str.pack(self.element.innerHTML); 64 | }, function (self, value) { 65 | Element.check(self); 66 | self.element.innerHTML = $.Str.unpack(value); 67 | }); 68 | 69 | Element.$def('css', function (self, name, value) { 70 | Element.check(self); 71 | if (value === builtins.NotImplemented) { 72 | return $.Str.pack(self.element.style[$.Str.unpack(name)]); 73 | } else { 74 | self.element.style[$.Str.unpack(name)] = $.Str.unpack(value, ''); 75 | } 76 | }, ['name', 'value'], {defaults: {'value': builtins.NotImplemented}}); 77 | 78 | Element.$def('append', function (self, other) { 79 | Element.check(self); 80 | Element.check(other); 81 | self.element.appendChild(other.element); 82 | }, ['other']); 83 | 84 | 85 | module.$def('get_body', function () { 86 | if (document.body) { 87 | if (document.body.__element__) { 88 | return document.body.__element__; 89 | } 90 | var element = new $.PyObject(Element); 91 | element.element = document.body; 92 | document.body.__element__ = element; 93 | return element; 94 | } else { 95 | $.raise(builtins.ValueError, 'unable to load body from dom'); 96 | } 97 | }); 98 | 99 | 100 | Element.$def('register_listener', function (self, name, callback) { 101 | Element.check(self); 102 | var element = self.element; 103 | element.addEventListener($.Str.unpack(name), function (event) { 104 | $.resume(callback, [self], {}); 105 | }); 106 | }, ['name', 'resume']); 107 | 108 | 109 | 110 | 111 | module.$def('set_interval', function (interval, callback) { 112 | var handle = $.Int.pack(setInterval(function () { 113 | $.resume(callback, [handle], {}); 114 | }, $.Int.unpack(interval))); 115 | return handle; 116 | }, ['interval', 'resume']); 117 | 118 | }, ['builtins']); -------------------------------------------------------------------------------- /modules/greenlet.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | jaspy.module('greenlet', function ($, module, builtins) { 17 | var Greenlet = module.$class('greenlet'); 18 | var GreenletExit = module.$class('GreenletExit', [builtins.Exception]); 19 | 20 | 21 | var current = new $.PyObject(Greenlet); 22 | current.started = true; 23 | current.bottom = {back: null}; 24 | 25 | Greenlet.$def('__new__', function (cls, run) { 26 | Greenlet.check_subclass(cls); 27 | var self = new $.PyObject(cls); 28 | self.run = run; 29 | self.started = false; 30 | self.setattr('parent', current); 31 | return self; 32 | }, ['run']); 33 | 34 | Greenlet.$def('switch', function (self, state, frame) { 35 | Greenlet.check(self); 36 | if (self.started) { 37 | switch (state) { 38 | case 0: 39 | current.top = frame; 40 | $.vm.frame = self.top; 41 | current = self; 42 | current.bottom.back = frame; 43 | return 1; 44 | case 1: 45 | return; 46 | } 47 | } else { 48 | current.top = frame; 49 | current = self; 50 | self.started = true; 51 | if (self.bottom = $.call(self.run)) { 52 | self.bottom.back = null; 53 | return 1; 54 | } 55 | } 56 | }); 57 | 58 | module.$def('getcurrent', function () { 59 | return current; 60 | }); 61 | 62 | }, ['builtins']); -------------------------------------------------------------------------------- /modules/time.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | jaspy.module('time', function ($, module, builtins) { 17 | module.$def('sleep', function (seconds, state, frame) { 18 | switch (state) { 19 | case 0: 20 | setTimeout(function () { 21 | $.resume(frame); 22 | }, $.Float.unpack(seconds) * 1000); 23 | $.suspend(); 24 | return 1; 25 | case 1: 26 | break; 27 | } 28 | }, ['seconds']); 29 | 30 | module.$def('time', function () { 31 | return $.Float.pack((new Date()).getTime() / 1000); 32 | }); 33 | }, ['builtins']); 34 | -------------------------------------------------------------------------------- /notes/README.rst: -------------------------------------------------------------------------------- 1 | Notes 2 | ===== 3 | 4 | This directory contains some notes about Jaspy. 5 | 6 | 7 | Execution Model 8 | =============== 9 | There are two different execution modes — synchronous and asynchronous. 10 | 11 | Example: 12 | 13 | Dict.set called in synchronous mode finishes execution and might call python code. 14 | 15 | Dict.set called in asynchronous mode returns a coroutine which should be yielded. 16 | 17 | This way it is possible to write function which work when called from JavaScript directly 18 | but also allow the Python interpreter to provide features like threading. When using the 19 | asm.js or speedy backend the interpreter is also in synchronous mode which allows to call 20 | Python code. Greenlets, debugging and threading and all other features which require the 21 | interpreter to be suspendable only work in asynchronous mode. 22 | 23 | Asynchronous functions have to be decorated using the coroutine decorator. 24 | 25 | Example: 26 | 27 | .. code:: javascript 28 | 29 | Dict.set = coroutine(function (self, key, value) { 30 | var hash = yield key.hash(); 31 | // do insertion 32 | }); 33 | 34 | 35 | Synchronous Mode: 36 | 37 | .. code:: javascript 38 | 39 | Dict.set(key, value) // blocks 40 | 41 | Asynchronous Mode: 42 | 43 | .. code:: javascript 44 | 45 | yield Dict.set(key, value) // blocks 46 | Dict.set(key, value) // is non-blocking 47 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jaspy", 3 | "version": "0.0.4", 4 | "description": "a Python VM written entirely from scratch in JavaScript with some unique features", 5 | "main": "jaspy.js", 6 | "directories": { 7 | "example": "example" 8 | }, 9 | "scripts": { 10 | "test": "node_modules/.bin/karma start .karma.js", 11 | "build": "python3 build.py", 12 | "prepublish": "python3 build.py && cp build/jaspy.js .", 13 | "publish": "rm -f jaspy.js", 14 | "start": "cd example && PYTHONPATH=../ python3 -m jaspy.cli --interactive --jaspy-js ../jaspy.js" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/koehlma/jaspy.git" 19 | }, 20 | "keywords": [ 21 | "python", 22 | "interpreter" 23 | ], 24 | "devDependencies": { 25 | "coveralls": "^2.11.4", 26 | "jasmine": "2.1.x", 27 | "jasmine-core": "^2.3.4", 28 | "karma": "^0.13.3", 29 | "karma-coverage": "^0.4.2", 30 | "karma-firefox-launcher": "^1.0.0", 31 | "karma-jasmine": "^0.3.6" 32 | }, 33 | "author": "Maximilian Köhl ", 34 | "license": "LGPL-3.0", 35 | "bugs": { 36 | "url": "https://github.com/koehlma/jaspy/issues" 37 | }, 38 | "homepage": "https://github.com/koehlma/jaspy#readme" 39 | } 40 | -------------------------------------------------------------------------------- /readthedocs.yml: -------------------------------------------------------------------------------- 1 | conda: 2 | file: doc/environment.yml -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright (C) 2016, Maximilian Köhl 4 | # 5 | # This program is free software: you can redistribute it and/or modify it under 6 | # the terms of the GNU Lesser General Public License version 3 as published by 7 | # the Free Software Foundation. 8 | # 9 | # This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | # PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License along 14 | # with this program. If not, see . 15 | 16 | import os 17 | 18 | from distutils.core import setup 19 | 20 | 21 | LICENSE = 'License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)' 22 | 23 | with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as readme: 24 | long_description = readme.read() 25 | 26 | 27 | setup( 28 | name='jaspy', 29 | version='0.1.0dev', 30 | description='Python interpreter in JavaScript', 31 | long_description=long_description, 32 | author='Maximilian Köhl', 33 | author_email='mail@koehlma.de', 34 | url='https://github.com/koehlma/jaspy', 35 | license='LGPLv3', 36 | scripts=['build'], 37 | packages=['jaspy'], 38 | extras_require={ 39 | 'interactive remote console': ['ptpython', 'pygments'] 40 | }, 41 | classifiers=[ 42 | 'Development Status :: 2 - Pre-Alpha', 43 | 'Intended Audience :: Developers', 44 | LICENSE, 45 | 'Operating System :: OS Independent', 46 | 'Programming Language :: Python :: 3', 47 | 'Environment :: Web Environment', 48 | 'Topic :: Internet', 49 | 'Topic :: Internet :: WWW/HTTP :: Browsers', 50 | ] 51 | ) 52 | -------------------------------------------------------------------------------- /spec/dict_spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | describe('Dict', function () { 18 | it('operations', function () { 19 | var dict = new jaspy.Dict(); 20 | 21 | dict.set('abc', 42); 22 | expect(dict.get('abc')).toBe(42); 23 | dict.set('abc', 31415); 24 | expect(dict.pop('abc')).toBe(31415); 25 | 26 | expect(dict.get('abc')).toBe(undefined); 27 | expect(dict.get('abc', 123)).toBe(123); 28 | }); 29 | 30 | it('entries', function () { 31 | var dict = new jaspy.Dict(); 32 | 33 | expect(dict.entries().length, 0); 34 | 35 | dict.set('abc', 42); 36 | dict.set('abc', 31415); 37 | 38 | expect(dict.entries().length, 1); 39 | expect(dict.entries()[0].key.equals('abc')).toBe(jaspy.True); 40 | expect(dict.entries()[0].value).toBe(31415); 41 | }); 42 | 43 | it('size', function () { 44 | var dict = new jaspy.Dict(); 45 | 46 | expect(dict.size).toBe(0); 47 | 48 | dict.set('abc', 42); 49 | dict.set('abc', 31415); 50 | dict.set('xyz', 4242); 51 | 52 | expect(dict.size).toBe(2); 53 | 54 | dict.pop('abc'); 55 | 56 | expect(dict.size).toBe(1); 57 | 58 | dict.pop('xyz'); 59 | 60 | expect(dict.size).toBe(0); 61 | }) 62 | }); -------------------------------------------------------------------------------- /spec/executor_spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | describe('Executor', function () { 18 | it('faculty', function () { 19 | var $ = jaspy; 20 | 21 | var fac = $._(function* (n) { 22 | return n > 1 ? n * (yield fac(n - 1)) : 1; 23 | }); 24 | 25 | expect(fac(10)).toBe(3628800); 26 | 27 | var thread = new $.Microthread(fac, 10); 28 | expect(thread.run()).toBe(3628800); 29 | }); 30 | 31 | it('suspension', function () { 32 | var $ = jaspy; 33 | 34 | var test = $._(function* () { 35 | var value = yield new $.Suspension(); 36 | return value; 37 | }); 38 | 39 | var thread = new $.Microthread(test); 40 | var suspension = thread.run(); 41 | expect(suspension instanceof $.Suspension).toBeTruthy(); 42 | suspension.set_result(42); 43 | expect(thread.run(suspension)).toBe(42); 44 | 45 | expect(function () { test(); }).toThrowError(/ExecutorError/); 46 | }); 47 | }); -------------------------------------------------------------------------------- /spec/float_spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Matthias Heerde 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | describe('jaspy.Float', function () { 18 | it('unpack', function () { 19 | expect(jaspy.Float.unpack(42.42)).toBe(42.42); 20 | expect(jaspy.Float.unpack(new jaspy.Int(42))).toBe(42); 21 | expect(function () { jaspy.Float.unpack(jaspy.None); }).toThrowError(/TypeError/); 22 | expect(function () { jaspy.Float.unpack(new jaspy.Str('42.42')); }).toThrowError(/TypeError/); 23 | expect(jaspy.Float.unpack(jaspy.None, 42.42)).toBe(42.42); 24 | }); 25 | 26 | it('equals', function() { 27 | expect((new jaspy.Float(42)).equals(42)).toBe(jaspy.True); 28 | expect((new jaspy.Float(42)).equals((new jaspy.Int(42)))).toBe(jaspy.True); 29 | expect((new jaspy.Float(42)).equals((new jaspy.Float(42)))).toBe(jaspy.True); 30 | }); 31 | 32 | it('add', function () { 33 | expect(((new jaspy.Float(21)).add(21)).equals(42)).toBe(jaspy.True); 34 | expect(((new jaspy.Float(21)).add(new jaspy.Float(21))).equals(42)).toBe(jaspy.True); 35 | expect(((new jaspy.Float(21)).add(new jaspy.Float(21))).equals(new jaspy.Float(42))).toBe(jaspy.True); 36 | }); 37 | 38 | it('is_integer', function () { 39 | expect((new jaspy.Float(42.42)).is_integer()).toBe(jaspy.False); 40 | }) 41 | }); -------------------------------------------------------------------------------- /spec/javascripts/support/function-bind.js: -------------------------------------------------------------------------------- 1 | Function.prototype.bind = Function.prototype.bind || function (bind_to) { 2 | var func = this; 3 | return function () { 4 | return func.apply(bind_to, arguments); 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /spec/javascripts/support/jasmine.yml: -------------------------------------------------------------------------------- 1 | src_files: 2 | - build/jaspy.js 3 | 4 | spec_files: 5 | - "**/*[Ss]pec.js" 6 | 7 | spec_dir: spec/ 8 | -------------------------------------------------------------------------------- /spec/spec.js: -------------------------------------------------------------------------------- 1 | describe('Jaspy', function () { 2 | it('test case 1', function () { 3 | expect(true).toBe(true); 4 | }) 5 | }); -------------------------------------------------------------------------------- /src/__init__.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | /** 18 | * Welcome to the source code of Jaspy! 19 | * ==================================== 20 | * Warning: Python interpreter under construction! 21 | * 22 | * So you like to learn how Jaspy works under the hood? You have made the first important 23 | * step — you are reading the source code of Jaspy. In case you only want to use Jaspy you 24 | * probably should instead read the user documentation [1]_. 25 | * 26 | * The in-source documentation is more or less a loose bunch of thoughts, ideas, and other 27 | * stuff than well-structured and well-conceived. However I will try to explain the rough 28 | * ideas and concepts behind the implementation. 29 | * 30 | * .. [1] https://jaspy.readthedocs.io/en/latest/ 31 | * 32 | * 33 | * Motivation 34 | * ---------- 35 | * Why should one come up with yet another Python-JavaScript-Thingamajig? Just to give you 36 | * a few examples why this might be a good idea: 37 | * 38 | * - First of all programming is fun, so why not? 39 | * - Learn how to build an interpreter and the corresponding runtime environment. 40 | * - Get a better and deep understanding of the Python programming language. 41 | * - Experiment with web technologies and see what is possible. 42 | * - Learn JavaScript although your really do not want to. ;) 43 | * 44 | * However I do not want to reinvent the wheel, therefore Jaspy is also an experiment on 45 | * how to implement various features others are not offering. This includes threading, a 46 | * built-in debugger, Greenlets, blocking IO, and other features. 47 | */ 48 | 49 | 50 | // << for lib in libs 51 | // #include '../libs/' + lib 52 | // >> 53 | 54 | 55 | var jaspy = {}; 56 | 57 | 58 | (function () { 59 | var $ = jaspy; 60 | 61 | // #include 'base.js' 62 | // #include 'executor.js' 63 | })(); 64 | 65 | 66 | // #include 'runtime/__init__.js' 67 | // #include 'language/__init__.js' 68 | 69 | // #include 'runtime/sys.js' 70 | 71 | /* {{ _builtins }} */ 72 | 73 | jaspy.main(jaspy.get_module('_builtins'), [], true); 74 | jaspy.update(jaspy.builtins, jaspy.get_module('_builtins').__dict__); 75 | jaspy.builtins['__name__'] = 'builtins'; 76 | 77 | // << if ENABLE_THREADING 78 | // #include '../modules/_thread.js' 79 | // >> 80 | 81 | // << for module in modules 82 | // #include '../modules/' + module + '.js' 83 | // >> 84 | -------------------------------------------------------------------------------- /src/base.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | $.update = function (target, source) { 18 | var name, value; 19 | for (name in source) { 20 | if (source.hasOwnProperty(name)) { 21 | target[name] = source[name]; 22 | } 23 | } 24 | }; 25 | 26 | 27 | $.error = function (message, type) { 28 | throw new Error((type || '[Fatal Error]') + ' ' + (message || 'Fatal interpreter error, unable to recover!')); 29 | }; 30 | 31 | 32 | $.assert = function (condition, message) { 33 | if (!condition) { 34 | $.error(message, '[Assertion Failed]'); 35 | } 36 | }; 37 | 38 | 39 | $.raise = function (exc_type, exc_value, exc_traceback) { 40 | if (exc_value) { 41 | console.error('Exception: ' + exc_value); 42 | } 43 | $.error('Exception raised before interpreter has been fully initialized!'); 44 | }; 45 | 46 | 47 | $.Class = function (attributes, superclass) { 48 | var constructor = attributes.constructor; 49 | if (superclass) { 50 | constructor.prototype = Object.create(superclass.prototype); 51 | } 52 | $.update(constructor.prototype, attributes); 53 | constructor.superclass = superclass; 54 | constructor.extend = function (attributes) { 55 | return $.Class(attributes, constructor); 56 | }; 57 | return constructor; 58 | }; 59 | 60 | $.Class.extend = function (attributes) { 61 | return $.Class(attributes); 62 | }; 63 | -------------------------------------------------------------------------------- /src/language/__init__.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | /** 17 | * use node.js for offline compilation... 18 | * 19 | * - Tokenizer 20 | * - Parser 21 | * - Backends 22 | * - Bytecode (needs VM) 23 | * - Speedy (only needs Runtime, no Threading, Debugging, Greenlets and IO) 24 | * - ASM.js (needs neither VM nor Runtime) 25 | * 26 | * Requires: base, runtime 27 | */ 28 | 29 | 30 | jaspy.language = (function () { 31 | var $ = jaspy; 32 | 33 | // #include 'tokenizer.js' 34 | // #include 'parser.js' 35 | 36 | return { 37 | tokenize: tokenizer.tokenize 38 | } 39 | })(); -------------------------------------------------------------------------------- /src/language/parser.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | -------------------------------------------------------------------------------- /src/runtime/__init__.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | jaspy.runtime = (function () { 18 | var $ = jaspy; 19 | var _ = $._; 20 | 21 | // << if ENABLE_DEBUGGER 22 | console.info('Jaspy Python Interpreter\nDebugging Mode!'); 23 | // >> 24 | 25 | // #include 'constants.js' 26 | // #include 'base.js' 27 | 28 | // #include 'future.js' 29 | 30 | // #include 'object.js' 31 | // #include 'type.js' 32 | 33 | // #include 'core/__init__.js' 34 | 35 | // #include 'code.js' 36 | // #include 'module.js' 37 | 38 | // #include 'bridge.js' 39 | 40 | // #include 'python/__init__.js' 41 | 42 | // #include 'dis.js' 43 | 44 | // #include 'frame.js' 45 | // #include 'execute.js' 46 | // #include 'vm.js' 47 | 48 | // << if ENABLE_THREADING 49 | // #include 'threading.js' 50 | // >> 51 | 52 | // << if ENABLE_DEBUGGER 53 | // #include 'debugger.js' 54 | // >> 55 | })(); 56 | -------------------------------------------------------------------------------- /src/runtime/base.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var Integer = bigInt; 18 | 19 | 20 | var SIPHASH_KEY = [Math.random() * Math.pow(2, 32) >>> 0, 21 | Math.random() * Math.pow(2, 32) >>> 0, 22 | Math.random() * Math.pow(2, 32) >>> 0, 23 | Math.random() * Math.pow(2, 32) >>> 0]; 24 | 25 | function siphash(string) { 26 | var hash = SipHash.hash(SIPHASH_KEY, Str.unpack(string)); 27 | return new Int(Integer(hash.h).shiftLeft(32).or(hash.l)); 28 | } 29 | 30 | 31 | function error(message) { 32 | throw new Error('[FATAL ERROR] ' + (message || 'fatal interpreter error')); 33 | } 34 | 35 | function raise(exc_type, exc_value, exc_tb) { 36 | error('exception occurred before jaspy has been fully initialized'); 37 | } 38 | 39 | function assert(condition, message) { 40 | if (!condition) { 41 | error(message || 'assertion failed'); 42 | } 43 | } 44 | 45 | function update(target, source) { 46 | var name, value; 47 | for (name in source) { 48 | if (source.hasOwnProperty(name)) { 49 | target[name] = source[name]; 50 | } 51 | } 52 | } 53 | 54 | 55 | function Class(attributes, superclass) { 56 | var constructor = attributes.constructor; 57 | if (superclass) { 58 | constructor.prototype = Object.create(superclass.prototype); 59 | } 60 | update(constructor.prototype, attributes); 61 | constructor.superclass = superclass; 62 | constructor.extend = function (attributes) { 63 | return Class(attributes, constructor); 64 | }; 65 | return constructor; 66 | } 67 | 68 | Class.extend = function (attributes) { 69 | return Class(attributes); 70 | }; 71 | 72 | 73 | function $Class(name, attributes, bases, superclass) { 74 | var constructor = Class(attributes, superclass || PyObject); 75 | constructor.cls = $class(name, bases); 76 | constructor.check = function (object) { 77 | if (!(object instanceof constructor)) { 78 | raise(TypeError, 'expected ' + name); 79 | } 80 | }; 81 | constructor.$def = constructor.cls.$def.bind(constructor.cls); 82 | 83 | constructor.$map = function (name, spec, options) { 84 | var args, index; 85 | options = options || {}; 86 | options.simple = true; 87 | spec = spec || []; 88 | args = new Array(spec.length); 89 | for (index = 0; index < spec.length; index++) { 90 | args[index] = ('arg' + index); 91 | } 92 | var source = "" + 93 | "(function (" + (['self'].concat(args)).join(', ') + ") {" + 94 | " if (!(self instanceof constructor)) {" + 95 | " raise(TypeError, 'invalid type of self in native method call');" + 96 | " }" + 97 | " return self." + name + "(" + args.join(', ') + ");" + 98 | "})"; 99 | constructor.cls.$def(name, eval(source), spec, options); 100 | }; 101 | constructor.extend = function (name, attributes, bases) { 102 | bases = bases || []; 103 | bases.push(constructor.cls); 104 | return $Class(name, attributes, bases, constructor); 105 | }; 106 | return constructor; 107 | } 108 | 109 | 110 | $.Integer = Integer; 111 | 112 | $.siphash = siphash; 113 | 114 | $.error = error; 115 | $.raise = raise; 116 | $.assert = assert; 117 | $.assign = update; 118 | 119 | $.update = update; 120 | 121 | $.Class = Class; 122 | -------------------------------------------------------------------------------- /src/runtime/bridge.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | function pack_bool(boolean) { 17 | return boolean ? True : False; 18 | } 19 | 20 | function pack_bytes(array) { 21 | return new Bytes(array); 22 | } 23 | 24 | function pack_tuple(array) { 25 | return new Tuple(array); 26 | } 27 | 28 | function pack_object(object) { 29 | return new PyJSObject(object); 30 | } 31 | 32 | function pack_array(array) { 33 | return new PyJSArray(array); 34 | } 35 | 36 | function pack_function(func) { 37 | return new PyJSFunction(func); 38 | } 39 | 40 | function pack_error(error) { 41 | return make_exception(JSError, '[' + error.name + '] ' + error.message); 42 | } 43 | 44 | function pack(object) { 45 | if (object == undefined) { 46 | return None; 47 | } 48 | if (object instanceof PyObject) { 49 | return object; 50 | } else if (typeof object == 'string') { 51 | return Str.pack(object); 52 | } else if (typeof object == 'number') { 53 | if (Number.isInteger(object)) { 54 | return Int.pack(object); 55 | } else { 56 | return Float.pack(object); 57 | } 58 | } else if (typeof object == 'boolean') { 59 | return pack_bool(object); 60 | } else if (typeof object == 'function') { 61 | return pack_function(object); 62 | } else if (object instanceof Array) { 63 | return pack_array(object); 64 | } else if (object instanceof Object) { 65 | return pack_object(object); 66 | } else { 67 | raise(TypeError, 'unable to pack native object'); 68 | } 69 | } 70 | 71 | function unpack_bool(object, fallback) { 72 | if ((object === None || !object) && fallback) { 73 | return fallback; 74 | } 75 | if (!(object.__class__ === py_bool)) { 76 | raise(TypeError, 'unable to unpack to_bool from object'); 77 | } 78 | return object === True; 79 | } 80 | 81 | function unpack_bytes(object, fallback) { 82 | if ((object === None || !object) && fallback) { 83 | return fallback; 84 | } 85 | if (!(object instanceof Bytes)) { 86 | raise(TypeError, 'unable to unpack bytes from object'); 87 | } 88 | return object.array; 89 | } 90 | 91 | function unpack_tuple(object, fallback) { 92 | if ((object === None || !object) && fallback) { 93 | return fallback; 94 | } 95 | if (!(object instanceof Tuple)) { 96 | raise(TypeError, 'unable to unpack tuple from object'); 97 | } 98 | return object.array; 99 | } 100 | 101 | function unpack_object(object, fallback) { 102 | if ((object === None || !object) && fallback) { 103 | return fallback; 104 | } 105 | if (!(object instanceof PyJSObject)) { 106 | raise(TypeError, 'unable to unpack js object'); 107 | } 108 | return object.object; 109 | } 110 | 111 | function unpack_array(object, fallback) { 112 | if ((object === None || !object) && fallback) { 113 | return fallback; 114 | } 115 | if (!(object instanceof PyJSArray)) { 116 | raise(TypeError, 'unable to unpack js array'); 117 | } 118 | return object.array; 119 | } 120 | 121 | function unpack_function(object, fallback) { 122 | if ((object === None || !object) && fallback) { 123 | return fallback; 124 | } 125 | if (!(object instanceof PyJSFunction)) { 126 | raise(TypeError, 'unable to unpack js function'); 127 | } 128 | return object.func; 129 | } 130 | 131 | function unpack(object, fallback) { 132 | if ((object === None || !object) && fallback) { 133 | return fallback; 134 | } 135 | if (!(object instanceof PyObject)) { 136 | raise(TypeError, 'object to unpack is not a python object'); 137 | } 138 | if (object.__class__ === py_bool) { 139 | return unpack_bool(object); 140 | } else if (object === None) { 141 | return null; 142 | } else if (object instanceof Int) { 143 | return Int.unpack(object); 144 | } else if (object instanceof Float) { 145 | return Float.unpack(object); 146 | } else if (object instanceof Str) { 147 | return Str.unpack(object); 148 | } else if (object instanceof Bytes) { 149 | return unpack_bytes(object); 150 | } else if (object instanceof Tuple) { 151 | return unpack_tuple(object); 152 | } else if (object instanceof PyJSObject) { 153 | return unpack_object(object); 154 | } else if (object instanceof PyJSArray) { 155 | return unpack_array(object); 156 | } else if (object instanceof PyJSFunction) { 157 | return unpack_function(object); 158 | } else { 159 | raise(TypeError, 'unable to unpack native value from object'); 160 | } 161 | } 162 | 163 | 164 | 165 | $.pack_bool = pack_bool; 166 | $.pack_bytes = pack_bytes; 167 | $.pack_tuple = pack_tuple; 168 | $.pack_object = pack_object; 169 | $.pack_array = pack_array; 170 | $.pack_function = pack_function; 171 | $.pack_error = pack_error; 172 | 173 | 174 | $.unpack_bool = unpack_bool; 175 | $.unpack_bytes = unpack_bytes; 176 | $.unpack_tuple = unpack_tuple; 177 | $.unpack_object = unpack_object; 178 | $.unpack_array = unpack_array; 179 | $.unpack_function = unpack_function; 180 | 181 | $.pack = pack; 182 | $.unpack = unpack; 183 | 184 | var Bool = { 185 | pack: pack_bool, 186 | unpack: unpack_bool 187 | }; 188 | -------------------------------------------------------------------------------- /src/runtime/constants.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | var TRACEBACK_ON_EXCEPTION = true; 17 | 18 | var CODE_FLAGS = { 19 | OPTIMIZED: 1 << 0, 20 | NEWLOCALS: 1 << 1, 21 | NESTED: 1 << 4, 22 | GENERATOR: 1 << 5, 23 | NOFREE: 1 << 6, 24 | 25 | STAR_ARGS: 1 << 2, 26 | STAR_KWARGS: 1 << 3, 27 | 28 | PYTHON: 1 << 10, 29 | NATIVE: 1 << 11 30 | }; 31 | 32 | var BLOCK_TYPES = { 33 | BASE: 0, 34 | LOOP: 1, 35 | EXCEPT: 2, 36 | FINALLY: 3 37 | }; 38 | 39 | var CAUSES = { 40 | RETURN: 0, 41 | EXCEPTION: 1, 42 | BREAK: 2, 43 | CONTINUE: 3, 44 | YIELD: 4, 45 | RUN: 5 46 | }; 47 | 48 | var COMPARE_OPS = { 49 | LT: 0, 50 | LE: 1, 51 | EQ: 2, 52 | NE: 3, 53 | GT: 4, 54 | GE: 5, 55 | IN: 6, 56 | NIN: 7, 57 | IS: 8, 58 | NIS: 9, 59 | EXC: 10 60 | }; 61 | 62 | var COMPARE_SLOTS = [ 63 | '__lt__', 64 | '__le__', 65 | '__eq__', 66 | '__ne__', 67 | '__gt__', 68 | '__ge__', 69 | '__contains__', 70 | '__contains__' 71 | ]; 72 | 73 | var OPCODES = { 74 | BEFORE_ASYNC_WITH: 52, 75 | BINARY_ADD: 23, 76 | BINARY_AND: 64, 77 | BINARY_FLOOR_DIVIDE: 26, 78 | BINARY_LSHIFT: 62, 79 | BINARY_MATRIX_MULTIPLY: 16, 80 | BINARY_MODULO: 22, 81 | BINARY_MULTIPLY: 20, 82 | BINARY_OR: 66, 83 | BINARY_POWER: 19, 84 | BINARY_RSHIFT: 63, 85 | BINARY_SUBSCR: 25, 86 | BINARY_SUBTRACT: 24, 87 | BINARY_TRUE_DIVIDE: 27, 88 | BINARY_XOR: 65, 89 | BREAK_LOOP: 80, 90 | BUILD_LIST: 103, 91 | BUILD_LIST_UNPACK: 149, 92 | BUILD_MAP: 105, 93 | BUILD_MAP_UNPACK: 150, 94 | BUILD_MAP_UNPACK_WITH_CALL: 151, 95 | BUILD_SET: 104, 96 | BUILD_SET_UNPACK: 153, 97 | BUILD_SLICE: 133, 98 | BUILD_TUPLE: 102, 99 | BUILD_TUPLE_UNPACK: 152, 100 | CALL_FUNCTION: 131, 101 | CALL_FUNCTION_KW: 141, 102 | CALL_FUNCTION_VAR: 140, 103 | CALL_FUNCTION_VAR_KW: 142, 104 | COMPARE_OP: 107, 105 | CONTINUE_LOOP: 119, 106 | DELETE_ATTR: 96, 107 | DELETE_DEREF: 138, 108 | DELETE_FAST: 126, 109 | DELETE_GLOBAL: 98, 110 | DELETE_NAME: 91, 111 | DELETE_SUBSCR: 61, 112 | DUP_TOP: 4, 113 | DUP_TOP_TWO: 5, 114 | END_FINALLY: 88, 115 | EXTENDED_ARG: 144, 116 | FOR_ITER: 93, 117 | GET_AITER: 50, 118 | GET_ANEXT: 51, 119 | GET_AWAITABLE: 73, 120 | GET_ITER: 68, 121 | GET_YIELD_FROM_ITER: 69, 122 | IMPORT_FROM: 109, 123 | IMPORT_NAME: 108, 124 | IMPORT_STAR: 84, 125 | INPLACE_ADD: 55, 126 | INPLACE_AND: 77, 127 | INPLACE_FLOOR_DIVIDE: 28, 128 | INPLACE_LSHIFT: 75, 129 | INPLACE_MATRIX_MULTIPLY: 17, 130 | INPLACE_MODULO: 59, 131 | INPLACE_MULTIPLY: 57, 132 | INPLACE_OR: 79, 133 | INPLACE_POWER: 67, 134 | INPLACE_RSHIFT: 76, 135 | INPLACE_SUBTRACT: 56, 136 | INPLACE_TRUE_DIVIDE: 29, 137 | INPLACE_XOR: 78, 138 | JUMP_ABSOLUTE: 113, 139 | JUMP_FORWARD: 110, 140 | JUMP_IF_FALSE_OR_POP: 111, 141 | JUMP_IF_TRUE_OR_POP: 112, 142 | LIST_APPEND: 145, 143 | LOAD_ATTR: 106, 144 | LOAD_BUILD_CLASS: 71, 145 | LOAD_CLASSDEREF: 148, 146 | LOAD_CLOSURE: 135, 147 | LOAD_CONST: 100, 148 | LOAD_DEREF: 136, 149 | LOAD_FAST: 124, 150 | LOAD_GLOBAL: 116, 151 | LOAD_NAME: 101, 152 | MAKE_CLOSURE: 134, 153 | MAKE_FUNCTION: 132, 154 | MAP_ADD: 147, 155 | NOP: 9, 156 | POP_BLOCK: 87, 157 | POP_EXCEPT: 89, 158 | POP_JUMP_IF_FALSE: 114, 159 | POP_JUMP_IF_TRUE: 115, 160 | POP_TOP: 1, 161 | PRINT_EXPR: 70, 162 | RAISE_VARARGS: 130, 163 | RETURN_VALUE: 83, 164 | ROT_THREE: 3, 165 | ROT_TWO: 2, 166 | SETUP_ASYNC_WITH: 154, 167 | SETUP_EXCEPT: 121, 168 | SETUP_FINALLY: 122, 169 | SETUP_LOOP: 120, 170 | SETUP_WITH: 143, 171 | SET_ADD: 146, 172 | STORE_ATTR: 95, 173 | STORE_DEREF: 137, 174 | STORE_FAST: 125, 175 | STORE_GLOBAL: 97, 176 | STORE_NAME: 90, 177 | STORE_SUBSCR: 60, 178 | UNARY_INVERT: 15, 179 | UNARY_NEGATIVE: 11, 180 | UNARY_NOT: 12, 181 | UNARY_POSITIVE: 10, 182 | UNPACK_EX: 94, 183 | UNPACK_SEQUENCE: 92, 184 | WITH_CLEANUP_FINISH: 82, 185 | WITH_CLEANUP_START: 81, 186 | YIELD_FROM: 72, 187 | YIELD_VALUE: 86 188 | }; 189 | 190 | var OPCODES_ARGUMENT = 90; 191 | 192 | var OPCODES_EXTRA = new Array(255); 193 | 194 | OPCODES_EXTRA[OPCODES.UNARY_POSITIVE] = '__pos__'; 195 | OPCODES_EXTRA[OPCODES.UNARY_NEGATIVE] = '__neg__'; 196 | OPCODES_EXTRA[OPCODES.UNARY_NOT] = '__not__'; 197 | OPCODES_EXTRA[OPCODES.UNARY_INVERT] = '__invert__'; 198 | OPCODES_EXTRA[OPCODES.GET_ITER] = '__iter__'; 199 | OPCODES_EXTRA[OPCODES.GET_YIELD_FROM_ITER] = '__iter__'; 200 | 201 | OPCODES_EXTRA[OPCODES.BINARY_POWER] = 'pow'; 202 | OPCODES_EXTRA[OPCODES.BINARY_MULTIPLY] = 'mul'; 203 | OPCODES_EXTRA[OPCODES.BINARY_MATRIX_MULTIPLY] = 'matmul'; 204 | OPCODES_EXTRA[OPCODES.BINARY_FLOOR_DIVIDE] = 'floordiv'; 205 | OPCODES_EXTRA[OPCODES.BINARY_TRUE_DIVIDE] = 'truediv'; 206 | OPCODES_EXTRA[OPCODES.BINARY_MODULO] = 'mod'; 207 | OPCODES_EXTRA[OPCODES.BINARY_ADD] = 'add'; 208 | OPCODES_EXTRA[OPCODES.BINARY_SUBTRACT] = 'sub'; 209 | OPCODES_EXTRA[OPCODES.BINARY_SUBSCR] = 'getitem'; 210 | OPCODES_EXTRA[OPCODES.BINARY_LSHIFT] = 'lshift'; 211 | OPCODES_EXTRA[OPCODES.BINARY_RSHIFT] = 'rshift'; 212 | OPCODES_EXTRA[OPCODES.BINARY_AND] = 'and'; 213 | OPCODES_EXTRA[OPCODES.BINARY_XOR] = 'xor'; 214 | OPCODES_EXTRA[OPCODES.BINARY_OR] = 'or'; 215 | 216 | OPCODES_EXTRA[OPCODES.INPLACE_POWER] = '__ipow__'; 217 | OPCODES_EXTRA[OPCODES.INPLACE_MULTIPLY] = '__imul__'; 218 | OPCODES_EXTRA[OPCODES.INPLACE_MATRIX_MULTIPLY] = '__imatmul__'; 219 | OPCODES_EXTRA[OPCODES.INPLACE_FLOOR_DIVIDE] = '__ifloordiv__'; 220 | OPCODES_EXTRA[OPCODES.INPLACE_TRUE_DIVIDE] = '__itruediv__'; 221 | OPCODES_EXTRA[OPCODES.INPLACE_MODULO] = '__imod__'; 222 | OPCODES_EXTRA[OPCODES.INPLACE_ADD] = '__iadd__'; 223 | OPCODES_EXTRA[OPCODES.INPLACE_SUBTRACT] = '__isub__'; 224 | OPCODES_EXTRA[OPCODES.INPLACE_LSHIFT] = '__ilshift__'; 225 | OPCODES_EXTRA[OPCODES.INPLACE_RSHIFT] = '__irshift__'; 226 | OPCODES_EXTRA[OPCODES.INPLACE_AND] = '__iand__'; 227 | OPCODES_EXTRA[OPCODES.INPLACE_XOR] = '__ixor__'; 228 | OPCODES_EXTRA[OPCODES.INPLACE_OR] = '__ior__'; 229 | OPCODES_EXTRA[OPCODES.DELETE_SUBSCR] = '__delitem__'; 230 | 231 | OPCODES_EXTRA[OPCODES.STORE_SUBSCR] = '__setitem__'; 232 | 233 | OPCODES_EXTRA[OPCODES.SETUP_LOOP] = BLOCK_TYPES.LOOP; 234 | OPCODES_EXTRA[OPCODES.SETUP_EXCEPT] = BLOCK_TYPES.EXCEPT; 235 | OPCODES_EXTRA[OPCODES.SETUP_FINALLY] = BLOCK_TYPES.FINALLY; 236 | -------------------------------------------------------------------------------- /src/runtime/core/__init__.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var py_object = PyType.native('object', []); 18 | var py_type = PyType.native('type', [py_object]); 19 | 20 | py_object.__class__ = py_type; 21 | py_type.__class__ = py_type; 22 | 23 | 24 | // #include 'iterator.js' 25 | 26 | // #include 'dict.js' 27 | 28 | // #include 'int.js' 29 | 30 | // #include 'float.js' 31 | 32 | // #include 'str.js' 33 | // #include 'bytes.js' 34 | 35 | // #include 'tuple.js' 36 | // #include 'list.js' 37 | 38 | // #include 'cell.js' 39 | 40 | // #include 'wrapper.js' 41 | 42 | // #include 'func.js' 43 | // #include 'generator.js' 44 | // #include 'method.js' 45 | 46 | // #include 'property.js' 47 | 48 | // #include 'slice.js' 49 | 50 | // #include 'exception.js' 51 | // #include 'traceback.js' 52 | -------------------------------------------------------------------------------- /src/runtime/core/bytes.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var SINGLE_QUOTES_CODE = 39; 18 | var DOUBLE_QUOTES_CODE = 34; 19 | 20 | 21 | var Bytes = $Class('bytes', { 22 | constructor: function (array, cls) { 23 | if (!(array instanceof Uint8Array)) { 24 | raise(TypeError, 'invalid type of native bytes initializer'); 25 | } 26 | PyObject.call(this, cls || Bytes.cls); 27 | this.array = array; 28 | }, 29 | 30 | bool: function () { 31 | return this.array.length != 0; 32 | }, 33 | 34 | get: function (offset) { 35 | return this.array[offset]; 36 | }, 37 | 38 | str: function () { 39 | var index; 40 | var result = []; 41 | // TODO: improve performance 42 | for (index = 0; index < this.array.length; index++) { 43 | result.push(String.fromCharCode(this.array[index])); 44 | } 45 | return new Str(result.join('')); 46 | }, 47 | 48 | __repr__: function () { 49 | return new Str('b' + this.str().repr().value); 50 | }, 51 | 52 | __hash__: function () { 53 | return siphash(this.str()); 54 | }, 55 | 56 | decode: function (encoding) { 57 | var decoder, result; 58 | if (!TextDecoder) { 59 | // Polyfill: https://github.com/inexorabletash/text-encoding 60 | raise(RuntimeError, 'browser does not support decoding, please enable the polyfill'); 61 | } 62 | try { 63 | decoder = new TextDecoder(encoding || 'utf-8', {fatal: true}); 64 | } catch (error) { 65 | raise(LookupError, 'unknown encoding: ' + encoding); 66 | } 67 | try { 68 | result = decoder.decode(this.array); 69 | } catch (error) { 70 | raise(UnicodeDecodeError, 'unable to decode bytes object, data is not valid'); 71 | } 72 | return result; 73 | } 74 | }); 75 | 76 | Bytes.prototype.toString = Bytes.prototype.repr; 77 | 78 | 79 | $.Bytes = Bytes; 80 | -------------------------------------------------------------------------------- /src/runtime/core/cell.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var Cell = $Class('cell', { 18 | constructor: function (object) { 19 | PyObject.call(this, Cell.cls); 20 | this.set(object || None); 21 | }, 22 | 23 | set: function (object) { 24 | if (!(object instanceof PyObject)) { 25 | raise(TypeError, 'only python objects can be stored in a cell'); 26 | } 27 | this.object = object; 28 | }, 29 | 30 | get: function () { 31 | return this.object; 32 | } 33 | }); 34 | 35 | 36 | $.Cell = Cell; 37 | -------------------------------------------------------------------------------- /src/runtime/core/dict.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var DICT_ITERATOR_TYPES = { 18 | KEYS: 0, 19 | VALUES: 1, 20 | ITEMS: 2 21 | }; 22 | 23 | 24 | var Dict = $Class('dict', { 25 | constructor: function (table, cls) { 26 | PyObject.call(this, cls || Dict.cls); 27 | if (!issubclass(this.__class__, Dict.cls)) { 28 | raise(TypeError, 'unable to create dict with non dict subclass'); 29 | } 30 | this.table = table || {}; 31 | this.size = 0; 32 | if (!(this.table instanceof Object)) { 33 | raise(TypeError, 'invalid type of native dict initializer'); 34 | } 35 | }, 36 | 37 | get: function (str_key, fallback) { 38 | if (str_key instanceof Str) { 39 | str_key = str_key.value; 40 | } else if (typeof str_key != 'string') { 41 | raise(TypeError, 'invalid native dict key type'); 42 | } 43 | var hash = siphash(str_key); 44 | var entry = this.table[hash]; 45 | while (entry) { 46 | if (entry.key instanceof Str && entry.key.value == str_key) { 47 | return entry.value; 48 | } 49 | entry = entry.next; 50 | } 51 | return fallback; 52 | }, 53 | 54 | set: function (str_key, value) { 55 | if (str_key instanceof Str) { 56 | str_key = str_key.value; 57 | } else if (typeof str_key != 'string') { 58 | raise(TypeError, 'invalid native dict key type'); 59 | } 60 | var hash = siphash(str_key); 61 | var entry = this.table[hash]; 62 | while (entry) { 63 | if (entry.key instanceof Str && entry.key.value == str_key) { 64 | entry.value = value; 65 | return; 66 | } 67 | entry = entry.next; 68 | } 69 | this.table[hash] = new Dict.Entry(new Str(str_key), value, this.table[hash]); 70 | this.size++; 71 | }, 72 | 73 | pop: function (str_key) { 74 | var hash, entry, previous; 75 | if (str_key instanceof Str) { 76 | str_key = str_key.value; 77 | } else if (typeof str_key != 'string') { 78 | raise(TypeError, 'invalid native dict key type'); 79 | } 80 | hash = siphash(str_key); 81 | entry = this.table[hash]; 82 | while (entry) { 83 | if (entry.key instanceof Str && entry.key.value == str_key) { 84 | if (previous) { 85 | previous.next = entry.next; 86 | } else if (entry.next) { 87 | this.table[hash] = entry.next; 88 | } else { 89 | delete this.table[hash]; 90 | } 91 | this.size--; 92 | return entry.value; 93 | } 94 | previous = entry; 95 | entry = entry.next; 96 | } 97 | }, 98 | 99 | entries: function () { 100 | var hash, entry; 101 | var entries = []; 102 | for (hash in this.table) { 103 | if (this.table.hasOwnProperty(hash)) { 104 | entry = this.table[hash]; 105 | while (entry) { 106 | entries.push(entry); 107 | entry = entry.next; 108 | } 109 | } 110 | } 111 | return entries; 112 | }, 113 | 114 | keys: function () { 115 | return new Dict.Keys(this); 116 | }, 117 | 118 | values: function () { 119 | return new Dict.Values(this); 120 | }, 121 | 122 | items: function () { 123 | return new Dict.Items(this); 124 | }, 125 | 126 | copy: function () { 127 | var hash, entry; 128 | var dict = new Dict(); 129 | dict.size = this.size; 130 | for (hash in this.table) { 131 | if (this.table.hasOwnProperty(hash)) { 132 | entry = this.table[hash]; 133 | while (entry) { 134 | dict.table[hash] = new Dict.Entry(entry.key, entry.value, dict.table[hash]); 135 | entry = entry.next; 136 | } 137 | } 138 | } 139 | return dict; 140 | }, 141 | 142 | clear: function () { 143 | this.table = {}; 144 | this.size = 0; 145 | }, 146 | 147 | __len__: function () { 148 | return new Int(this.size); 149 | }, 150 | 151 | __hash__: function () { 152 | return Int.MINUSONE; 153 | } 154 | }); 155 | 156 | 157 | Dict.Entry = Class({ 158 | constructor: function (key, value, next) { 159 | this.key = key; 160 | this.value = value; 161 | this.next = next; 162 | } 163 | }); 164 | 165 | 166 | Dict.Iterator = Iterator.extend('dict_iterator', { 167 | constructor: function (dict, type) { 168 | PyObject.call(this, Dict.Iterator.cls); 169 | this.entries = dict.entries(); 170 | this.position = 0; 171 | this.type = type; 172 | }, 173 | 174 | next: function () { 175 | var entry; 176 | if (entry = this.entries[this.position++]) { 177 | switch (this.type) { 178 | case DICT_ITERATOR_TYPES.KEYS: 179 | return entry.key; 180 | case DICT_ITERATOR_TYPES.VALUES: 181 | return entry.value; 182 | case DICT_ITERATOR_TYPES.ITEMS: 183 | return new Tuple([entry.key, entry.value]); 184 | } 185 | } 186 | } 187 | }); 188 | 189 | 190 | Dict.Keys = $Class('dict_keys', { 191 | constructor: function (dict) { 192 | PyObject.call(this, Dict.Values.cls); 193 | this.dict = dict; 194 | }, 195 | 196 | __len__: function () { 197 | return this.dict.size; 198 | }, 199 | 200 | __iter__: function () { 201 | return new Dict.Iterator(this.dict, DICT_ITERATOR_TYPES.KEYS); 202 | } 203 | }); 204 | 205 | 206 | Dict.Values = $Class('dict_values', { 207 | constructor: function (dict) { 208 | PyObject.call(this, Dict.Values.cls); 209 | this.dict = dict; 210 | }, 211 | 212 | __len__: function () { 213 | return this.dict.size; 214 | }, 215 | 216 | __iter__: function () { 217 | return new Dict.Iterator(this.dict, DICT_ITERATOR_TYPES.VALUES); 218 | } 219 | }); 220 | 221 | 222 | Dict.Items = $Class('dict_items', { 223 | constructor: function (dict) { 224 | PyObject.call(this, Dict.Items.cls); 225 | this.dict = dict; 226 | }, 227 | 228 | __len__: function () { 229 | return this.dict.size; 230 | }, 231 | 232 | __iter__: function () { 233 | return new Dict.Iterator(this.dict, DICT_ITERATOR_TYPES.ITEMS); 234 | } 235 | }); 236 | 237 | 238 | 239 | $.Dict = Dict; 240 | -------------------------------------------------------------------------------- /src/runtime/core/exception.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var Exception = $Class('BaseException', { 18 | constructor: function (args, cls) { 19 | PyObject.call(this, cls || Exception.cls); 20 | this.args = args; 21 | } 22 | }); 23 | 24 | 25 | function make_exception(cls, message) { 26 | var exc_value = new PyObject(cls, {}); 27 | exc_value.__dict__['args'] = pack_tuple([Str.pack(message)]); 28 | return exc_value; 29 | } 30 | 31 | 32 | 33 | 34 | function format_exception(exc_value) { 35 | var string = []; 36 | if (exc_value.traceback) { 37 | string.push(format_traceback(exc_value.traceback)); 38 | } 39 | if (exc_value.getattr('args') instanceof Tuple && exc_value.getattr('args').array[0] instanceof Str) { 40 | string.push(exc_value.__class__.name + ': ' + exc_value.getattr('args').array[0]); 41 | } else { 42 | string.push(exc_value.__class__.name); 43 | } 44 | return string.join('\n'); 45 | } 46 | 47 | function print_exception(exc_value) { 48 | console.error(format_exception(exc_value)); 49 | } 50 | 51 | $.Exception = Exception; 52 | -------------------------------------------------------------------------------- /src/runtime/core/float.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * Copyright (C) 2016, Matthias Heerde 4 | * 5 | * This program is free software: you can redistribute it and/or modify it under 6 | * the terms of the GNU Lesser General Public License version 3 as published by 7 | * the Free Software Foundation. 8 | * 9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 10 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 11 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 12 | * 13 | * You should have received a copy of the GNU Lesser General Public License along 14 | * with this program. If not, see . 15 | */ 16 | 17 | 18 | var Float = $Class('float', { 19 | constructor: function (value, cls) { 20 | if (!(typeof value == 'number')) { 21 | raise(TypeError, 'invalid type of native float initializer'); 22 | } 23 | PyObject.call(this, cls || Float.cls); 24 | this.value = value; 25 | }, 26 | 27 | __abs__: function () { 28 | return new Float(Math.abs(this.value)); 29 | }, 30 | 31 | to_number: function () { 32 | return this.value; 33 | }, 34 | 35 | equals: function(other) { 36 | other = Float.unpack(other); 37 | return this.value == other ? True : False; 38 | }, 39 | 40 | add: function(other) { 41 | other = Float.unpack(other); 42 | return new Float(this.value + other); 43 | }, 44 | 45 | is_integer: function() { 46 | return Math.floor(this.value) == this.value ? True : False; 47 | }, 48 | 49 | as_integer_ratio: function() { 50 | if (this.value == Number.POSITIVE_INFINITY || this.value == Number.NEGATIVE_INFINITY) { 51 | raise(OverflowError, 'Cannot pass infinity to float.as_integer_ratio.'); 52 | } 53 | if (!Number.isFinite(this.value)) { 54 | raise(ValueError, 'Cannot pass NaN to float.as_integer_ratio.'); 55 | } 56 | raise(NotImplemented); 57 | } 58 | }); 59 | 60 | Float.pack = function (value) { 61 | return new Float(value); 62 | }; 63 | 64 | 65 | Float.unpack = function (object, fallback) { 66 | if ((object === None || !object) && fallback != undefined) { 67 | return fallback; 68 | } 69 | if (typeof object == 'number') { 70 | return object; 71 | } 72 | if (!(object instanceof Int) && !(object instanceof Float)) { 73 | raise(TypeError, 'unable to unpack number from object'); 74 | } 75 | return object.to_number(); 76 | }; 77 | 78 | $.Float = Float; 79 | -------------------------------------------------------------------------------- /src/runtime/core/func.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var Func = $Class('function', { 18 | constructor: function (name, code, options, cls) { 19 | PyObject.call(this, cls || Func.cls); 20 | 21 | this.name = name; 22 | this.code = code; 23 | 24 | this.qualname = options.qualname || this.name; 25 | this.doc = options.doc || ''; 26 | this.module = options.module || 'builtins'; 27 | this.defaults = options.defaults || null; 28 | this.closure = options.closure || null; 29 | this.globals = options.globals || null; 30 | } 31 | }); 32 | 33 | 34 | function $def(func, signature, options) { 35 | options = options || {}; 36 | var name = options.name || ''; 37 | var code = new NativeCode(func, options, signature); 38 | return new Func(name, code, options); 39 | } 40 | 41 | $.Func = Func; 42 | 43 | $.$def = $def; 44 | -------------------------------------------------------------------------------- /src/runtime/core/generator.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var Generator = $Class('generator', { 18 | constructor: function (code, frame) { 19 | PyObject.call(this, Generator.cls); 20 | this.code = code; 21 | this.frame = frame; 22 | } 23 | }); 24 | 25 | 26 | $.Generator = Generator; 27 | -------------------------------------------------------------------------------- /src/runtime/core/int.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | var Int = $Class('int', { 17 | constructor: function (value, cls) { 18 | PyObject.call(this, cls || Int.cls); 19 | try { 20 | this.value = bigInt(value); 21 | } catch (error) { 22 | raise(TypeError, 'invalid type of native int initializer'); 23 | } 24 | }, 25 | 26 | toString: function () { 27 | return this.value.toString(); 28 | }, 29 | 30 | valueOf: function () { 31 | return this.number(); 32 | }, 33 | 34 | to_bool: function () { 35 | return this.value.neq(0); 36 | }, 37 | 38 | to_string: function () { 39 | return this.value.toString(); 40 | }, 41 | 42 | is: function (other) { 43 | if (other instanceof Int) { 44 | return this.value.eq(other.value); 45 | } 46 | return false; 47 | }, 48 | 49 | to_number: function () { 50 | var number = this.value.toJSNumber(); 51 | if (number == Infinity || number == -Infinity) { 52 | raise(OverflowError, 'int too large to convert to float'); 53 | } 54 | return number; 55 | }, 56 | 57 | float: function () { 58 | return new Float(this.to_number()); 59 | }, 60 | 61 | 62 | __abs__: function () { 63 | return new Int(this.value.abs()); 64 | }, 65 | 66 | __pos__: function () { 67 | return this; 68 | }, 69 | 70 | __neg__: function () { 71 | return new Int(this.value.negate()); 72 | }, 73 | 74 | __invert__: function () { 75 | return new Int(this.value.not()); 76 | }, 77 | 78 | __add__: function (other) { 79 | return new Int(this.value.add(other.value)); 80 | }, 81 | 82 | __sub__: function (other) { 83 | return new Int(this.value.subtract(other.value)); 84 | }, 85 | 86 | __pow__: function (other) { 87 | if (other < 0) { 88 | return Float.pack(Math.pow(this.to_number(), other.to_number())); 89 | } 90 | return new Int(this.value.pow(other.value)); 91 | }, 92 | 93 | __mul__: function (other) { 94 | return new Int(this.value.multiply(other.value)); 95 | }, 96 | 97 | __floordiv__: function (other) { 98 | return new Int(this.value.divide(other.value)); 99 | }, 100 | 101 | __truediv__: function (other) { 102 | return Float.pack(this.to_number() / other.to_number()); 103 | }, 104 | 105 | __mod__: function (other) { 106 | if (!this.value.sign && other.value.sign) { 107 | return new Int(this.value.mod(other.value).substract(other.value)); 108 | } else if (this.value.sign && !other.value.sign) { 109 | return new Int(other.value.substract(this.value.mod(other.value))); 110 | } 111 | return new Int(this.value.mod(other.value)); 112 | }, 113 | 114 | __lshift__: function (other) { 115 | return new Int(this.value.shiftLeft(other.value)); 116 | }, 117 | 118 | 119 | __rshift__: function (other) { 120 | return new Int(this.value.shiftRight(other.value)); 121 | }, 122 | 123 | __and__: function (other) { 124 | return new Int(this.value.and(other.value)); 125 | }, 126 | 127 | __xor__: function (other) { 128 | return new Int(this.value.xor(other.value)); 129 | }, 130 | 131 | __or__: function (other) { 132 | return new Int(this.value.or(other.value)); 133 | }, 134 | 135 | __lt__: function (other) { 136 | return this.value.lt(other.value); 137 | }, 138 | 139 | __le__: function (other) { 140 | return this.value.leq(other.value); 141 | }, 142 | 143 | __eq__: function (other) { 144 | if (!(other instanceof Int)) { 145 | return false; 146 | } 147 | return this.value.eq(other.value); 148 | }, 149 | 150 | __ne__: function (other) { 151 | return this.value.neq(other.value); 152 | }, 153 | 154 | __gt__: function (other) { 155 | return this.value.gt(other.value); 156 | }, 157 | 158 | __ge__: function (other) { 159 | return this.value.geq(other.value); 160 | }, 161 | 162 | __hash__: function () { 163 | if (this.value.eq(-1)) { 164 | return Int.pack(-2); 165 | } else { 166 | return this; 167 | } 168 | } 169 | }); 170 | 171 | 172 | Int.parse = function (string, base) { 173 | if (base instanceof Int) { 174 | return new Int(bigInt(string, base.value)); 175 | } 176 | raise(TypeError, 'invalid type of integer base'); 177 | }; 178 | 179 | Int.unpack = function (object, fallback) { 180 | if ((object === None || object == undefined) && fallback != undefined) { 181 | return fallback; 182 | } 183 | if (object instanceof Int) { 184 | return object.to_number(); 185 | } else if (typeof object == 'number') { 186 | return object | 0; 187 | } else { 188 | raise(TypeError, 'unable to unpack integer from object'); 189 | } 190 | }; 191 | 192 | Int.pack = function (value) { 193 | return new Int(value); 194 | }; 195 | 196 | Int.ZERO = new Int(0); 197 | Int.ONE = new Int(1); 198 | Int.MINUSONE = new Int(-1); 199 | 200 | $.Int = Int; 201 | -------------------------------------------------------------------------------- /src/runtime/core/iterator.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var Iterator = $Class('iterator', { 18 | constructor: function (cls) { 19 | PyObject.call(this, cls); 20 | }, 21 | 22 | next: function () { 23 | raise(NotImplemented, 'next not implemented by native iterator'); 24 | }, 25 | 26 | __next__: function () { 27 | var value = this.next(); 28 | if (!value) { 29 | raise(StopIteration); 30 | } 31 | return value; 32 | }, 33 | 34 | __iter__: function () { 35 | return this; 36 | } 37 | }); 38 | 39 | 40 | $.Iterator = Iterator; 41 | -------------------------------------------------------------------------------- /src/runtime/core/list.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var List = $Class('list', { 18 | constructor: function (initializer, size, cls) { 19 | PyObject.call(this, cls || List.cls); 20 | this.value = new Array(4); 21 | if (initializer) { 22 | if (!(initializer instanceof Array)) { 23 | raise(TypeError, 'invalid type of list initializer'); 24 | } 25 | this.size = initializer.length; 26 | } else { 27 | this.size = size || 0; 28 | } 29 | this.grow(); 30 | if (initializer) { 31 | for (var index = 0; index < initializer.length; index++) { 32 | this.value[index] = initializer[index]; 33 | } 34 | } 35 | }, 36 | 37 | check: function (index) { 38 | if (index < 0) { 39 | index = this.size - index; 40 | } 41 | if (index < 0 || index > this.size - 1) { 42 | raise(IndexError, 'index out of range'); 43 | } 44 | return index; 45 | }, 46 | 47 | grow: function () { 48 | while (this.value.length <= this.size) { 49 | var length = this.value.length * 2; 50 | while (length <= this.size) { 51 | length *= 2; 52 | } 53 | this.value.length = length; 54 | } 55 | }, 56 | 57 | shrink: function () { 58 | if (this.value.length > 4 && this.value.length / 4 >= this.size) { 59 | var length = this.value.length / 2; 60 | while (length / 4 >= this.size && length > 4) { 61 | length /= 2; 62 | } 63 | this.value.length = length; 64 | } 65 | }, 66 | 67 | get: function (index) { 68 | index = this.check(index); 69 | return this.value[index] || None; 70 | }, 71 | 72 | set: function (index, item) { 73 | index = this.check(index); 74 | return this.value[index] = item; 75 | }, 76 | 77 | append: function (item) { 78 | this.size++; 79 | this.grow(); 80 | this.value[this.size - 1] = item; 81 | return item; 82 | }, 83 | 84 | pop: function (index) { 85 | index = this.check(index); 86 | this.size--; 87 | if (index == null) { 88 | index = this.size; 89 | } 90 | var item = this.value[index]; 91 | for (; index < this.size; index++) { 92 | this.value[index] = this.value[index + 1]; 93 | } 94 | this.value[index] = null; 95 | this.shrink(); 96 | return item; 97 | }, 98 | 99 | clear: function () { 100 | this.value = new Array(4); 101 | this.size = 0; 102 | }, 103 | 104 | slice: function (start, stop, step) { 105 | var index, list = new List(); 106 | if (start == undefined) { 107 | start = 0; 108 | } else if (start < 0) { 109 | start = this.size + start; 110 | } 111 | if (stop == undefined) { 112 | stop = this.size; 113 | } else if (stop < 0) { 114 | stop = this.size + stop; 115 | } 116 | step = step || 1; 117 | if (step > 0) { 118 | if (start < 0) { 119 | start = 0; 120 | } 121 | if (stop > this.size) { 122 | stop = this.size; 123 | } 124 | for (index = start; index < stop; index += step) { 125 | list.append(this.value[index]); 126 | } 127 | } else if (step < 0) { 128 | if (start >= this.size) { 129 | start = this.size - 1; 130 | } 131 | if (stop < 0) { 132 | stop = 0; 133 | } 134 | for (index = start; index > stop; index += step) { 135 | list.append(this.value[index]); 136 | } 137 | } else { 138 | raise(ValueError, 'slice step cannot be zero') 139 | } 140 | return list; 141 | }, 142 | 143 | concat: function (list_or_array) { 144 | var list, index, size; 145 | if (list_or_array instanceof List) { 146 | size = list_or_array.size; 147 | list_or_array = list_or_array.value; 148 | } else if (list_or_array instanceof Array) { 149 | size = list_or_array.length; 150 | } else { 151 | raise(TypeError, 'invalid type of concatenation object'); 152 | } 153 | list = new List(null, this.size + size); 154 | for (index = 0; index < this.size; index++) { 155 | list.value[index] = this.value[index]; 156 | } 157 | for (index = 0; index < size; index++) { 158 | list.value[index + this.size] = list_or_array[index]; 159 | } 160 | return list; 161 | }, 162 | 163 | copy: function () { 164 | return this.concat([]); 165 | }, 166 | 167 | __iter__: function () { 168 | return new List.Iterator(this); 169 | }, 170 | 171 | __len__: function () { 172 | return new Int(this.size); 173 | }, 174 | 175 | __hash__: function () { 176 | return Int.MINUSONE; 177 | }, 178 | 179 | __mul__: function (other) { 180 | if (!(other instanceof Int)) { 181 | raise(UnsupportedOperation, 'unsupported operation') 182 | } 183 | var result = new List(); 184 | var iterations = Int.unpack(other); 185 | for (var iteration = 0; iteration < iterations; iteration++) { 186 | for (var index = 0; index < this.size; index++) { 187 | result.append(this.value[index]); 188 | } 189 | } 190 | return result; 191 | } 192 | }); 193 | 194 | 195 | List.Iterator = Iterator.extend('list_iterator', { 196 | constructor: function (list) { 197 | Iterator.call(this, List.Iterator.cls); 198 | this.list = list; 199 | this.position = 0; 200 | }, 201 | 202 | next: function () { 203 | if (this.position < this.list.size) { 204 | return this.list.value[this.position++]; 205 | } 206 | } 207 | }); 208 | 209 | 210 | $.List = List; 211 | -------------------------------------------------------------------------------- /src/runtime/core/method.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var Method = $Class('method', { 18 | constructor: function (self, func) { 19 | PyObject.call(this, Method.cls); 20 | this.self = self; 21 | this.func = func; 22 | } 23 | }); 24 | 25 | -------------------------------------------------------------------------------- /src/runtime/core/property.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var Property = $Class('property', { 18 | constructor: function (getter, setter) { 19 | PyObject.call(this, Property.cls, { 20 | 'fget': getter || None, 21 | 'fset': setter || None 22 | }); 23 | this.getter = getter; 24 | this.setter = setter; 25 | } 26 | }); 27 | 28 | function new_property(getter, setter) { 29 | return new Property(getter, setter); 30 | } 31 | -------------------------------------------------------------------------------- /src/runtime/core/slice.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var Slice = $Class('slice', { 18 | constructor: function (start, stop, step) { 19 | // TODO: check step 20 | 21 | PyObject.call(this, Slice.cls); 22 | 23 | this.start = start; 24 | this.stop = stop; 25 | this.step = step || None; 26 | }, 27 | 28 | normalize: function (length) { 29 | var start, stop, step; 30 | length = Int.unpack(length); 31 | step = Int.unpack(this.step, 1); 32 | if (step == 0) { 33 | raise(ValueError, 'slice step must not be zero'); 34 | } 35 | start = Int.unpack(this.start, 0); 36 | stop = Int.unpack(this.stop, length); 37 | if (start < 0) { 38 | start += length; 39 | } 40 | if (stop < 0) { 41 | stop += length; 42 | } 43 | return new Slice(Math.max(0, start), Math.min(stop, length), step); 44 | } 45 | 46 | }); 47 | 48 | 49 | function new_slice(start, stop, step) { 50 | return new Slice(start, stop, step); 51 | } 52 | 53 | $.Slice = Slice; 54 | -------------------------------------------------------------------------------- /src/runtime/core/traceback.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var Traceback = $Class('traceback', { 18 | constructor: function (frame, position, line, next) { 19 | PyObject.call(this, Traceback.cls); 20 | this.frame = frame; 21 | this.position = position; 22 | this.line = line; 23 | this.next = next || None; 24 | } 25 | }); 26 | 27 | 28 | function format_traceback(traceback) { 29 | var string = ['Traceback (most recent call last):']; 30 | while (traceback && traceback != None) { 31 | string.push(' File \'' + traceback.frame.code.filename + '\', line ' + traceback.line + ', in ' + traceback.frame.code.name); 32 | traceback = traceback.next; 33 | } 34 | return string.join('\n'); 35 | } 36 | 37 | function print_traceback(traceback) { 38 | console.error(format_traceback(traceback)); 39 | } 40 | 41 | 42 | $.Traceback = Traceback; 43 | -------------------------------------------------------------------------------- /src/runtime/core/tuple.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var Tuple = $Class('tuple', { 18 | constructor: function (array, cls) { 19 | if (array instanceof Tuple) { 20 | array = array.array; 21 | } 22 | if (!(array instanceof Array)) { 23 | raise(TypeError, 'invalid type of native tuple initializer'); 24 | } 25 | PyObject.call(this, cls || Tuple.cls); 26 | this.array = array; 27 | Object.freeze(this.array); 28 | }, 29 | 30 | get: function (index) { 31 | return this.array[index]; 32 | }, 33 | 34 | len: function () { 35 | return this.array.length; 36 | }, 37 | 38 | __iter__: function () { 39 | return new Tuple.Iterator(this); 40 | } 41 | }); 42 | 43 | 44 | Tuple.Iterator = Iterator.extend('tuple_iterator', { 45 | constructor: function (tuple) { 46 | PyObject.call(this, Tuple.Iterator.cls); 47 | this.tuple = tuple; 48 | this.position = 0; 49 | }, 50 | 51 | next: function () { 52 | if (this.position < this.tuple.array.length) { 53 | return this.tuple.array[this.position++]; 54 | } 55 | } 56 | }); 57 | 58 | Tuple.unpack = function (object, fallback) { 59 | if ((object === None || !object) && fallback !== undefined) { 60 | return fallback; 61 | } 62 | if (object instanceof Tuple) { 63 | return object.array; 64 | } else if (Array.isArray(object)) { 65 | return object; 66 | } else { 67 | raise(TypeError, 'unable to unpack array from object'); 68 | } 69 | }; 70 | 71 | Tuple.EMPTY = new Tuple([]); 72 | 73 | $.Tuple = Tuple; 74 | -------------------------------------------------------------------------------- /src/runtime/core/wrapper.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var PyJSObject = PyObject.extend({ 18 | constructor: function (object, cls) { 19 | if (!(object instanceof Object)) { 20 | raise(TypeError, 'invalid type of native object initializer'); 21 | } 22 | PyObject.call(this, cls || py_js_object); 23 | this.object = object; 24 | }, 25 | 26 | primitive: function () { 27 | return this.object; 28 | } 29 | }); 30 | 31 | 32 | var PyJSArray = PyObject.extend({ 33 | constructor: function (array, cls) { 34 | if (!(array instanceof Array)) { 35 | raise(TypeError, 'invalid type of native array initializer'); 36 | } 37 | PyObject.call(this, cls || py_js_array); 38 | this.array = array; 39 | }, 40 | 41 | primitive: function () { 42 | return this.array; 43 | } 44 | }); 45 | 46 | 47 | var PyJSFunction = PyObject.extend({ 48 | constructor: function (func, cls) { 49 | if (typeof func != 'function') { 50 | raise(TypeError, 'invalid type of native function initializer'); 51 | } 52 | PyObject.call(this, cls || py_js_function); 53 | this.func = func; 54 | }, 55 | 56 | primitive: function () { 57 | return this.func; 58 | } 59 | }); 60 | 61 | 62 | $.PyJSObject = PyJSObject; 63 | $.PyJSArray = PyJSArray; 64 | $.PyJSFunction = PyJSFunction; 65 | -------------------------------------------------------------------------------- /src/runtime/dis.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | function disassemble(code) { 17 | var instruction, opcode, high, low, argument, index; 18 | 19 | var instructions = []; 20 | var table = {}; 21 | var position = 0; 22 | 23 | while (position < code.bytecode.length) { 24 | instruction = {start: position}; 25 | table[position] = instruction; 26 | 27 | opcode = code.bytecode.charCodeAt(position++); 28 | if (opcode >= OPCODES_ARGUMENT) { 29 | low = code.bytecode.charCodeAt(position++); 30 | high = code.bytecode.charCodeAt(position++); 31 | argument = high << 8 | low; 32 | } 33 | if (opcode === OPCODES.EXTENDED_ARG) { 34 | opcode = code.bytecode.charCodeAt(position++); 35 | low = code.bytecode.charCodeAt(position++); 36 | high = code.bytecode.charCodeAt(position++); 37 | argument = (argument << 16) | (high << 8) | low; 38 | } 39 | instruction.position = instructions.length; 40 | instruction.end = position; 41 | instruction.opcode = opcode; 42 | instruction.argument = argument; 43 | instructions.push(instruction); 44 | } 45 | 46 | for (index = 0; index < instructions.length; index++) { 47 | instruction = instructions[index]; 48 | switch (instruction.opcode) { 49 | case OPCODES.JUMP_FORWARD: 50 | instruction.opcode = OPCODES.JUMP_ABSOLUTE; 51 | case OPCODES.FOR_ITER: 52 | case OPCODES.SETUP_EXCEPT: 53 | case OPCODES.SETUP_FINALLY: 54 | case OPCODES.SETUP_LOOP: 55 | case OPCODES.SETUP_WITH: 56 | instruction.target = table[instruction.end + instruction.argument].position; 57 | break; 58 | 59 | case OPCODES.JUMP_IF_FALSE_OR_POP: 60 | case OPCODES.JUMP_IF_TRUE_OR_POP: 61 | case OPCODES.POP_JUMP_IF_FALSE: 62 | case OPCODES.POP_JUMP_IF_TRUE: 63 | case OPCODES.JUMP_ABSOLUTE: 64 | instruction.target = table[instruction.argument].position; 65 | break; 66 | } 67 | } 68 | 69 | return instructions; 70 | } 71 | 72 | 73 | $.disassemble = disassemble; 74 | -------------------------------------------------------------------------------- /src/runtime/future.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var Future = Class.extend({ 18 | constructor: function () { 19 | this.result = null; 20 | 21 | this.success = false; 22 | this.error = false; 23 | 24 | this.callbacks = []; 25 | }, 26 | 27 | done: function (callback) { 28 | if (this.error || this.success) { 29 | callback(this); 30 | } else { 31 | this.callbacks.push(callback); 32 | } 33 | }, 34 | 35 | run_callbacks: function () { 36 | for (var index = 0; index < this.callbacks.length; index++) { 37 | this.callbacks[index](this) 38 | } 39 | }, 40 | 41 | set_result: function (result) { 42 | this.result = result; 43 | this.success = true; 44 | this.run_callbacks(); 45 | }, 46 | 47 | set_exception: function (exception) { 48 | this.result = exception; 49 | this.error = true; 50 | this.run_callbacks(); 51 | } 52 | }); 53 | 54 | 55 | $.Future = Future; 56 | -------------------------------------------------------------------------------- /src/runtime/module.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | var modules = {}; 17 | var pending = {}; 18 | 19 | function get_module(name) { 20 | if (name in modules) { 21 | return modules[name]; 22 | } else { 23 | raise(ImportError, 'no module named \'' + name + '\''); 24 | } 25 | } 26 | 27 | function get_namespace(name) { 28 | return get_module(name).__dict__; 29 | } 30 | 31 | function register_module(name, module) { 32 | modules[name] = module; 33 | module.__dict__['__name__'] = Str.pack(name); 34 | } 35 | 36 | function unregister_module(name) { 37 | delete modules[name]; 38 | } 39 | 40 | 41 | var Module = Class.extend({ 42 | constructor: function (name, depends) { 43 | this.name = name; 44 | this.depends = depends || []; 45 | this.__dict__ = {}; 46 | if (this.name) { 47 | register_module(this.name, this); 48 | } 49 | this.wrapper = null; 50 | } 51 | }); 52 | 53 | 54 | var PythonModule = Module.extend({ 55 | constructor: function (name, code, depends) { 56 | Module.call(this, name, depends); 57 | this.code = code; 58 | this.frame = null; 59 | } 60 | }); 61 | 62 | 63 | var NativeModule = Module.extend({ 64 | constructor: function (name, func, depends) { 65 | Module.call(this, name, depends); 66 | this.func = func; 67 | if (func) { 68 | func.apply(null, [jaspy, this].concat(this.depends.map(get_namespace))); 69 | } 70 | }, 71 | 72 | $def: function (name, func, signature, options) { 73 | options = options || {}; 74 | signature = signature || []; 75 | options.module = this.name; 76 | options.name = name; 77 | options.qualname = name; 78 | options.simple = func.length == signature.length; 79 | this.__dict__[name] = $def(func, signature, options); 80 | return this.__dict__[name]; 81 | }, 82 | 83 | $set: function (name, value) { 84 | this.__dict__[name] = value; 85 | return this.__dict__[name]; 86 | }, 87 | 88 | $class: function (name, bases, mcs) { 89 | this.__dict__[name] = PyType.native(name, bases, null, mcs); 90 | return this.__dict__[name]; 91 | } 92 | }); 93 | 94 | function module(name, initializer, depends) { 95 | if (typeof initializer == 'function') { 96 | return new NativeModule(name, initializer, depends); 97 | } else if (initializer instanceof PythonCode) { 98 | return new PythonModule(name, initializer, depends); 99 | } else { 100 | throw new Error('invalid type of code or function'); 101 | } 102 | } 103 | 104 | 105 | $.get_module = get_module; 106 | $.get_namespace = get_namespace; 107 | 108 | $.module = module; 109 | 110 | $.modules = modules; 111 | -------------------------------------------------------------------------------- /src/runtime/object.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var object_id_counter = 0; 18 | 19 | /** 20 | * Python Object 21 | * ============= 22 | * Base class of all Python objects. 23 | * 24 | * __class___: PyType 25 | * Type of the Python object. 26 | * 27 | * __addr__: null | JS.Number 28 | * Virtual memory address of the object. 29 | * 30 | * __dict__: Dict | JS.Object 31 | * Mapping containing instance attributes. 32 | */ 33 | var PyObject = Class.extend({ 34 | constructor: function (cls, dict) { 35 | this.__class__ = cls; 36 | this.__addr__ = null; 37 | this.__dict__ = dict === undefined ? {} : dict; 38 | }, 39 | 40 | get_id: function () { 41 | if (this.__addr__ === null) { 42 | this.__addr__ = object_id_counter++; 43 | } 44 | return this.__addr__; 45 | }, 46 | 47 | get_address: function () { 48 | return ('0000000000000' + this.get_id().toString(16)).substr(-13); 49 | }, 50 | 51 | call: function (name, args, kwargs) { 52 | var method = this.__class__.lookup(name); 53 | if (method) { 54 | if (isinstance(method, py_classmethod)) { 55 | return call(method.func, [this.__class__].concat(args || []), kwargs); 56 | } else if (isinstance(method, py_staticmethod)) { 57 | return call(method.func, args, kwargs); 58 | } else if (name == '__new__') { 59 | return call(method, [this.__class__].concat(args || []), kwargs); 60 | } 61 | return call(method, [this].concat(args || []), kwargs); 62 | } else { 63 | vm.return_value = null; 64 | vm.last_exception = METHOD_NOT_FOUND; 65 | return false; 66 | } 67 | }, 68 | 69 | setattr: function (name, value) { 70 | if (!this.__dict__) { 71 | raise(TypeError, 'object does not support attribute access'); 72 | } 73 | if (name instanceof Str) { 74 | name = name.value; 75 | } else if (typeof name != 'string') { 76 | raise(TypeError, 'native attribute name must be a string'); 77 | } 78 | if (this.__dict__ instanceof Dict) { 79 | return this.__dict__.set(name, value); 80 | } else { 81 | this.__dict__[name] = value; 82 | } 83 | }, 84 | 85 | getattr: function (name) { 86 | if (!this.__dict__) { 87 | raise(TypeError, 'object does not support attribute access'); 88 | } 89 | if (name instanceof Str) { 90 | name = name.value; 91 | } else if (typeof name != 'string') { 92 | raise(TypeError, 'native attribute name must be a string'); 93 | } 94 | if (this.__dict__ instanceof Dict) { 95 | return this.__dict__.get(name); 96 | } else { 97 | return this.__dict__[name]; 98 | } 99 | }, 100 | 101 | is: function (other) { 102 | return this === other; 103 | }, 104 | 105 | 106 | /* low level implicit conversions */ 107 | 108 | to_bool: function () { 109 | return true; 110 | }, 111 | 112 | to_number: function () { 113 | raise(TypeError, 'unable to convert object to native number implicitly'); 114 | }, 115 | 116 | to_string: function () { 117 | raise(TypeError, 'unable to convert object to native string implicitly'); 118 | }, 119 | 120 | 121 | /* expose special methods to native code */ 122 | 123 | repr: function () { 124 | return this.__repr__(); 125 | }, 126 | 127 | str: function () { 128 | return this.__str__(); 129 | }, 130 | 131 | len: function () { 132 | return this.__len__(); 133 | }, 134 | 135 | iter: function () { 136 | return this.__iter__(); 137 | }, 138 | 139 | add: function (other) { 140 | return this.__add__(other); 141 | }, 142 | 143 | sub: function (other) { 144 | return this.__sub__(other); 145 | }, 146 | 147 | mul: function (other) { 148 | return this.__mul__(other); 149 | }, 150 | 151 | truediv: function (other) { 152 | return this.__truediv__(other); 153 | }, 154 | 155 | floordiv: function (other) { 156 | return this.__floordiv__(other); 157 | }, 158 | 159 | mod: function (other) { 160 | return this.__mod__(other); 161 | }, 162 | 163 | divmod: function (other) { 164 | return this.__divmod__(other); 165 | }, 166 | 167 | lshift: function (other) { 168 | return this.__lshift__(other); 169 | }, 170 | 171 | rshift: function (other) { 172 | return this.__rshift__(other); 173 | }, 174 | 175 | and: function (other) { 176 | return this.__and__(other); 177 | }, 178 | 179 | xor: function (other) { 180 | return this.__xor__(other); 181 | }, 182 | 183 | or: function (other) { 184 | return this.__or__(other); 185 | }, 186 | 187 | neg: function () { 188 | return this.__neg__(); 189 | }, 190 | 191 | pos: function () { 192 | return this.__pos__(); 193 | }, 194 | 195 | abs: function () { 196 | return this.__abs__(); 197 | }, 198 | 199 | invert: function () { 200 | return this.__invert__(); 201 | }, 202 | 203 | hash: function () { 204 | return this.__hash__(); 205 | }, 206 | 207 | eq: function (other) { 208 | return this.__eq__(other); 209 | }, 210 | 211 | ne: function (other) { 212 | return this.__ne__(other); 213 | }, 214 | 215 | gt: function (other) { 216 | return this.__gt__(other); 217 | }, 218 | 219 | ge: function (other) { 220 | return this.__ge__(other); 221 | }, 222 | 223 | lt: function (other) { 224 | return this.__lt__(other); 225 | }, 226 | 227 | le: function (other) { 228 | return this.__le__(other); 229 | }, 230 | 231 | 232 | /* special methods */ 233 | 234 | __repr__: function () { 235 | return '<' + this.__class__.name + ' object at 0x' + this.get_address() + '>'; 236 | }, 237 | 238 | __str__: function () { 239 | return this.__repr__(); 240 | }, 241 | 242 | __hash__: function () { 243 | return siphash(this.get_address()); 244 | } 245 | }); 246 | 247 | PyObject.prototype.toString = PyObject.prototype.repr; 248 | 249 | 250 | function isinstance(object, cls) { 251 | var index; 252 | for (index = 0; index < object.__class__.mro.length; index++) { 253 | if (object.__class__.mro[index] === cls) { 254 | return true; 255 | } 256 | } 257 | return false; 258 | } 259 | 260 | 261 | $.PyObject = PyObject; 262 | 263 | $.isinstance = isinstance; 264 | -------------------------------------------------------------------------------- /src/runtime/python/__init__.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | // #include 'boot.js' 17 | 18 | // #include 'object.js' 19 | // #include 'type.js' 20 | // #include 'dict.js' 21 | // #include 'int.js' 22 | // #include 'bool.js' 23 | // #include 'float.js' 24 | // #include 'str.js' 25 | // #include 'bytes.js' 26 | // #include 'list.js' 27 | // #include 'function.js' 28 | // #include 'generator.js' 29 | // #include 'method.js' 30 | // #include 'property.js' 31 | // #include 'slice.js' 32 | // #include 'module.js' 33 | // #include 'none.js' 34 | // #include 'exception.js' 35 | // #include 'iterator.js' 36 | 37 | // #include 'tuple.js' 38 | 39 | // #include 'builtins.js' 40 | 41 | // #include 'helpers.js' 42 | 43 | // #include 'wrapper.js' 44 | -------------------------------------------------------------------------------- /src/runtime/python/bool.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | var TRUE_STR = Str.pack('True'); 17 | var FALSE_STR = Str.pack('False'); 18 | 19 | py_bool.$def('__new__', function (cls, initializer, state, frame) { 20 | switch (state) { 21 | case 0: 22 | // FIXME: subclassing bool is not allowed 23 | if (!(issubclass(cls, py_bool))) { 24 | raise(TypeError, 'class is not an subclass of to_bool'); 25 | } 26 | if (initializer.call('__bool__')) { 27 | return 1; 28 | } 29 | case 1: 30 | if (except(MethodNotFoundError)) { 31 | if (initializer.call('__len__')) { 32 | return 2; 33 | } 34 | } else { 35 | if (vm.return_value && vm.return_value.__class__ !== py_bool) { 36 | raise(TypeError, '__bool__ should return bool'); 37 | } 38 | return vm.return_value; 39 | } 40 | case 2: 41 | if (except(MethodNotFoundError)) { 42 | return True; 43 | } else if (vm.return_value) { 44 | // FIXME: create object of class cls 45 | return vm.return_value.ne(False) ? True : False; 46 | } 47 | } 48 | }, ['initializer'], {defaults: {initializer: False}}); 49 | 50 | py_bool.$def('__str__', function (self) { 51 | return self.ne(False) ? TRUE_STR : FALSE_STR; 52 | }); 53 | -------------------------------------------------------------------------------- /src/runtime/python/boot.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | var py_bool = PyType.native('to_bool', [Int.cls]); 18 | 19 | var py_js_object = PyType.native('JSObject'); 20 | var py_js_array = PyType.native('JSArray', [py_js_object]); 21 | var py_js_function = PyType.native('JSFunction', [py_js_object]); 22 | 23 | var py_traceback = PyType.native('traceback'); 24 | 25 | var py_set = PyType.native('set'); 26 | var py_frozenset = PyType.native('frozenset'); 27 | 28 | var py_classmethod = PyType.native('classmethod'); 29 | var py_staticmethod = PyType.native('staticmethod'); 30 | 31 | var py_module = PyType.native('ModuleType'); 32 | 33 | var None = new PyObject(PyType.native('NoneType')); 34 | var NotImplemented = new PyObject(PyType.native('NotImplemented')); 35 | var Ellipsis = new PyObject(PyType.native('Ellipsis')); 36 | 37 | var False = new Int(0, py_bool); 38 | var True = new Int(1, py_bool); 39 | 40 | var py_base_exception = PyType.native('BaseException'); 41 | var py_exception = new PyType('Exception', [py_base_exception]); 42 | var ValueError = new PyType('ValueError', [py_exception]); 43 | var ArithmeticError = new PyType('ArithmeticError', [py_exception]); 44 | var LookupError = new PyType('LookupError', [py_exception]); 45 | var RuntimeError = new PyType('RuntimeError', [py_exception]); 46 | var BufferError = new PyType('BufferError', [py_exception]); 47 | var AssertionError = new PyType('AssertionError', [py_exception]); 48 | var AttributeError = new PyType('AttributeError', [py_exception]); 49 | var EOFError = new PyType('EOFError', [py_exception]); 50 | var FloatingPointError = new PyType('FloatingPointError', [ArithmeticError]); 51 | var GeneratorExit = new PyType('GeneratorExit', [py_base_exception]); 52 | var ImportError = new PyType('ImportError', [py_exception]); 53 | var IndexError = new PyType('IndexError', [LookupError]); 54 | var KeyError = new PyType('KeyError', [py_exception]); 55 | var KeyboardInterrupt = new PyType('KeyboardInterrupt', [py_base_exception]); 56 | var MemoryError = new PyType('MemoryError', [py_exception]); 57 | var NameError = new PyType('NameError', [py_exception]); 58 | var NotImplementedError = new PyType('NotImplementedError', [RuntimeError]); 59 | var OSError = new PyType('OSError', [py_exception]); 60 | var OverflowError = new PyType('OverflowError', [py_exception]); 61 | var RecursionError = new PyType('RecursionError', [RuntimeError]); 62 | var ReferenceError = new PyType('ReferenceError', [py_exception]); 63 | var StopIteration = new PyType('StopIteration', [py_exception]); 64 | var SyntaxError = new PyType('SyntaxError', [py_exception]); 65 | var IndentationError = new PyType('IndentationError', [SyntaxError]); 66 | var TabError = new PyType('TabError', [IndentationError]); 67 | var SystemError = new PyType('SystemError', [py_exception]); 68 | var SystemExit = new PyType('SystemExit', [py_base_exception]); 69 | var TypeError = new PyType('TypeError', [py_exception]); 70 | var UnboundLocalError = new PyType('UnboundLocalError', [NameError]); 71 | var UnicodeError = new PyType('UnicodeError', [ValueError]); 72 | var UnicodeEncodeError = new PyType('UnicodeEncodeError', [UnicodeError]); 73 | var UnicodeDecodeError = new PyType('UnicodeDecodeError', [UnicodeError]); 74 | var UnicodeTranslateError = new PyType('UnicodeTranslateError', [UnicodeError]); 75 | var ZeroDivisionError = new PyType('ZeroDivisionError', [ArithmeticError]); 76 | var EnvironmentError = OSError; 77 | var IOError = OSError; 78 | 79 | var BlockingIOError = new PyType('BlockingIOError', [OSError]); 80 | var ChildProcessError = new PyType('ChildProcessError', [OSError]); 81 | var BrokenPipeError = new PyType('BrokenPipeError', [OSError]); 82 | var ConnectionError = new PyType('ConnectionError', [OSError]); 83 | var ConnectionAbortedError = new PyType('ConnectionAbortedError', [ConnectionError]); 84 | var ConnectionRefusedError = new PyType('ConnectionRefusedError', [ConnectionError]); 85 | var ConnectionResetError = new PyType('ConnectionResetError', [ConnectionError]); 86 | var FileExistsError = new PyType('FileExistsError', [OSError]); 87 | var FileNotFoundError = new PyType('FileNotFoundError', [OSError]); 88 | var InterruptedError = new PyType('InterruptedError', [OSError]); 89 | var IsADirectoryError = new PyType('IsADirectoryError', [OSError]); 90 | var NotADirectoryError = new PyType('NotADirectoryError', [OSError]); 91 | var PermissionError = new PyType('PermissionError', [OSError]); 92 | var ProcessLookupError = new PyType('ProcessLookupError', [OSError]); 93 | var TimeoutError = new PyType('TimeoutError', [OSError]); 94 | 95 | var MethodNotFoundError = new PyType('MethodNotFoundError', [TypeError]); 96 | var METHOD_NOT_FOUND = { 97 | exc_type: MethodNotFoundError, 98 | exc_value: make_exception(MethodNotFoundError, 'method not found'), 99 | exc_tb: None 100 | }; 101 | 102 | var UnpackError = new PyType('UnpackError', [TypeError]); 103 | var PackError = new PyType('PackError', [TypeError]); 104 | 105 | var ExecutorError = new PyType('ExecutorError', [RuntimeError]); 106 | 107 | var JSError = new PyType('JSError', [py_exception]); 108 | 109 | var ZERO = new Int(0); 110 | var ONE = new Int(1); 111 | 112 | var EMPTY_TUPLE = new Tuple([]); 113 | 114 | $.ExecutorError = ExecutorError; 115 | -------------------------------------------------------------------------------- /src/runtime/python/bytes.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | Bytes.cls.$def('decode', function (self, encoding) { 17 | Bytes.__class__.check(self); 18 | return Str.pack(self.decode(Str.unpack(encoding))); 19 | }, ['encoding'], {defaults: {'encoding': None}}); 20 | 21 | 22 | Bytes.$map('__hash__'); 23 | -------------------------------------------------------------------------------- /src/runtime/python/exception.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | py_base_exception.$def('__new__', function (cls, args) { 17 | py_base_exception.check_subclass(cls); 18 | return new PyObject(cls, {'args': pack_tuple(args)}) 19 | }, ['*args']); 20 | 21 | py_exception.$def('__init__', function (self, args) { 22 | self.setattr('args', pack_tuple(args)); 23 | }, ['*args']); -------------------------------------------------------------------------------- /src/runtime/python/float.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | Float.cls.$def('__new__', function (cls, initializer, state, frame) { 17 | switch (state) { 18 | case 0: 19 | if (!(subclass(cls, Float.cls))) { 20 | raise(TypeError, 'class is not an subclass of float'); 21 | } 22 | if (initializer instanceof Int || initializer instanceof Float) { 23 | if (initializer.__class__ == cls) { 24 | return initializer; 25 | } else { 26 | return Int.pack(initializer.value, cls); 27 | } 28 | } 29 | if (initializer instanceof Str) { 30 | return Float.pack(parseFloat(initializer.value)); 31 | } 32 | if (initializer.call('__float__')) { 33 | return 1; 34 | } 35 | case 1: 36 | if (except(MethodNotFoundError)) { 37 | raise(TypeError, 'invalid type of int initializer'); 38 | } else if (vm.return_value) { 39 | return vm.return_value; 40 | } 41 | break; 42 | } 43 | }, ['initializer'], {defaults: {initializer: Float.pack(0)}}); 44 | 45 | Float.cls.$def('__str__', function (self) { 46 | return Str.pack(Float.unpack(self).toString()); 47 | }); 48 | 49 | Float.cls.$def('__neg__', function (self) { 50 | return Float.pack(-Float.unpack(self)); 51 | }); 52 | 53 | Float.cls.$def('__pos__', function (self) { 54 | return self; 55 | }); 56 | 57 | Float.cls.$def('__lt__', function (self, other) { 58 | return Float.unpack(self) < Float.unpack(other) ? True : False; 59 | }, ['other']); 60 | 61 | Float.cls.$def('__le__', function (self, other) { 62 | return Float.unpack(self) <= Float.unpack(other) ? True : False; 63 | }, ['other']); 64 | 65 | Float.cls.$def('__eq__', function (self, other) { 66 | return Float.unpack(self) == Float.unpack(other) ? True : False; 67 | }, ['other']); 68 | 69 | Float.cls.$def('__ne__', function (self, other) { 70 | return Float.unpack(self) != Float.unpack(other) ? True : False; 71 | }, ['other']); 72 | 73 | Float.cls.$def('__gt__', function (self, other) { 74 | return Float.unpack(self) > Float.unpack(other) ? True : False; 75 | }, ['other']); 76 | 77 | Float.cls.$def('__ge__', function (self, other) { 78 | return Float.unpack(self) <= Float.unpack(other) ? True : False; 79 | }, ['other']); 80 | 81 | Float.cls.$def('__pow__', function (self, other) { 82 | return Float.pack(Math.pow(Float.unpack(self), Float.unpack(other))); 83 | }, ['other']); 84 | Float.cls.$def_alias('__pow__', '__ipow__'); 85 | Float.cls.$def_alias('__pow__', '__rpow__'); 86 | 87 | Float.cls.$def('__mul__', function (self, other) { 88 | return Float.pack(Float.unpack(self) * Float.unpack(other)); 89 | }, ['other']); 90 | Float.cls.$def_alias('__mul__', '__imul__'); 91 | Float.cls.$def_alias('__mul__', '__rmul__'); 92 | 93 | Float.cls.$def('__floordiv__', function (self, other) { 94 | return Int.pack(Math.floor(Float.unpack(self) / Float.unpack(other))); 95 | }, ['other']); 96 | Float.cls.$def_alias('__floordiv__', '__ifloordiv__'); 97 | Float.cls.$def_alias('__floordiv__', '__rfloordiv__'); 98 | 99 | Float.cls.$def('__truediv__', function (self, other) { 100 | return Float.pack(Float.unpack(self) / Float.unpack(other)); 101 | }, ['other']); 102 | Float.cls.$def_alias('__truediv__', '__itruediv__'); 103 | Float.cls.$def_alias('__truediv__', '__rtruediv__'); 104 | 105 | Float.cls.$def('__mod__', function (self, other) { 106 | return Float.pack(Float.unpack(self) % Float.unpack(other)); 107 | }, ['other']); 108 | Float.cls.$def_alias('__mod__', '__imod__'); 109 | Float.cls.$def_alias('__mod__', '__rmod__'); 110 | 111 | Float.cls.$def('__add__', function (self, other) { 112 | return Float.pack(Float.unpack(self) + Float.unpack(other)); 113 | }, ['other']); 114 | Float.cls.$def_alias('__add__', '__iadd__'); 115 | Float.cls.$def_alias('__add__', '__radd__'); 116 | 117 | Float.cls.$def('__sub__', function (self, other) { 118 | return Float.pack(Float.unpack(self) - Float.unpack(other)); 119 | }, ['other']); 120 | Float.cls.$def_alias('__sub__', '__isub__'); 121 | Float.cls.$def_alias('__sub__', '__rsub__'); 122 | 123 | 124 | Float.$map('__abs__'); 125 | -------------------------------------------------------------------------------- /src/runtime/python/function.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | Func.cls.$def('__get__', function (self, instance, owner) { 17 | return new Method(instance, self); 18 | }, ['instance', 'owner']); -------------------------------------------------------------------------------- /src/runtime/python/generator.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | Generator.cls.$def('__iter__', function (self) { 17 | return self; 18 | }); 19 | 20 | Generator.cls.$def('__next__', function (self, state, frame) { 21 | switch (state) { 22 | case 0: 23 | Generator.cls.check(self); 24 | self.running = true; 25 | self.frame.back = frame; 26 | vm.frame = self.frame; 27 | vm.return_value = None; 28 | return 1; 29 | case 1: 30 | if (self.frame.why == CAUSES.YIELD) { 31 | return vm.return_value; 32 | } else if (self.frame.why == CAUSES.RETURN) { 33 | raise(StopIteration, new PyObject(StopIteration, {'args': pack_tuple([vm.return_value])})); 34 | } 35 | } 36 | }); -------------------------------------------------------------------------------- /src/runtime/python/helpers.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | var unpack_sequence = $def(function (sequence, limit, state, frame) { 17 | while (true) { 18 | switch (state) { 19 | case 0: 20 | frame.result = []; 21 | frame.limit = Int.unpack(limit, Infinity); 22 | if (sequence.__class__.lookup('__next__')) { 23 | frame.iterator = sequence; 24 | state = 2; 25 | break; 26 | } 27 | if (sequence.call('__iter__')) { 28 | return 1; 29 | } 30 | case 1: 31 | if (!vm.return_value) { 32 | if (except(MethodNotFoundError)) { 33 | raise(TypeError, 'object does not support the iterable protocol'); 34 | } 35 | return; 36 | } 37 | frame.iterator = vm.return_value; 38 | case 2: 39 | if (frame.iterator.call('__next__')) { 40 | return 3; 41 | } 42 | case 3: 43 | if (!vm.return_value) { 44 | if (except(StopIteration)) { 45 | state = 4; 46 | break; 47 | } else if (except(MethodNotFoundError)) { 48 | raise(TypeError, 'object does not support the iterable protocol'); 49 | } 50 | return; 51 | } 52 | frame.result.push(vm.return_value); 53 | if (frame.result.length > frame.limit) { 54 | raise(ValueError, 'too many values to unpack (expected ' + frame.limit + ')'); 55 | } 56 | state = 2; 57 | break; 58 | case 4: 59 | return pack_tuple(frame.result); 60 | } 61 | } 62 | }, ['sequence', 'limit'], {name: 'unpack_sequence', defaults: {'limit': None}}); 63 | -------------------------------------------------------------------------------- /src/runtime/python/int.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | Int.cls.$def('__new__', function (cls, initializer, base, state, frame) { 17 | switch (state) { 18 | case 0: 19 | if (!(issubclass(cls, Int.cls))) { 20 | raise(TypeError, 'class is not an subclass of int'); 21 | } 22 | if (initializer instanceof Float) { 23 | return Int.pack(Math.floor(initializer.value), cls); 24 | } 25 | if (initializer instanceof Int) { 26 | if (initializer.__class__ == cls) { 27 | return initializer; 28 | } else { 29 | return Int.pack(initializer.value, cls); 30 | } 31 | } 32 | if (initializer instanceof Str) { 33 | return Int.parse(initializer.value, base); 34 | } 35 | if (initializer.call('__int__')) { 36 | return 1; 37 | } 38 | case 1: 39 | if (except(MethodNotFoundError)) { 40 | raise(TypeError, 'invalid type of int initializer'); 41 | } else if (vm.return_value) { 42 | return vm.return_value; 43 | } 44 | break; 45 | } 46 | }, ['initializer', 'base'], {defaults: {initializer: Int.pack(0), base: Int.pack(10)}}); 47 | 48 | Int.cls.$def('__str__', function (self) { 49 | return Str.pack(self.toString()); 50 | }); 51 | 52 | Int.cls.$def_alias('__str__', '__repr__'); 53 | 54 | 55 | Int.cls.$def('__bool__', function (self) { 56 | return pack_bool(self.ne(False)); 57 | }); 58 | 59 | Int.$def('__not__', function (self) { 60 | return pack_bool(!self.ne(False)); 61 | }); 62 | 63 | Int.cls.$def('__neg__', function (self) { 64 | return self.neg(); 65 | }); 66 | 67 | Int.cls.$def('__pos__', function (self) { 68 | return self; 69 | }); 70 | 71 | Int.cls.$def('__lt__', function (self, other) { 72 | return pack_bool(self.lt(other)); 73 | }, ['other']); 74 | 75 | Int.cls.$def('__le__', function (self, other) { 76 | return pack_bool(self.le(other)); 77 | }, ['other']); 78 | 79 | Int.cls.$def('__eq__', function (self, other) { 80 | return pack_bool(self.eq(other)); 81 | }, ['other']); 82 | 83 | Int.cls.$def('__ne__', function (self, other) { 84 | return pack_bool(self.ne(other)); 85 | }, ['other']); 86 | 87 | Int.cls.$def('__gt__', function (self, other) { 88 | return pack_bool(self.gt(other)); 89 | }, ['other']); 90 | 91 | Int.cls.$def('__ge__', function (self, other) { 92 | return pack_bool(self.ge(other)); 93 | }, ['other']); 94 | 95 | Int.cls.$def('__pow__', function (self, other) { 96 | return self.pow(other); 97 | }, ['other']); 98 | Int.cls.$def_alias('__pow__', '__ipow__'); 99 | Int.cls.$def_alias('__pow__', '__rpow__'); 100 | 101 | Int.cls.$def('__mul__', function (self, other) { 102 | return self.mul(other); 103 | }, ['other']); 104 | Int.cls.$def_alias('__mul__', '__imul__'); 105 | Int.cls.$def_alias('__mul__', '__rmul__'); 106 | 107 | Int.cls.$def('__floordiv__', function (self, other) { 108 | return self.floordiv(other); 109 | }, ['other']); 110 | Int.cls.$def_alias('__floordiv__', '__ifloordiv__'); 111 | Int.cls.$def_alias('__floordiv__', '__rfloordiv__'); 112 | 113 | Int.cls.$def('__truediv__', function (self, other) { 114 | return self.truediv(other); 115 | }, ['other']); 116 | Int.cls.$def_alias('__truediv__', '__itruediv__'); 117 | Int.cls.$def_alias('__truediv__', '__rtruediv__'); 118 | 119 | Int.cls.$def('__mod__', function (self, other) { 120 | return self.mod(other); 121 | }, ['other']); 122 | Int.cls.$def_alias('__mod__', '__imod__'); 123 | Int.cls.$def_alias('__mod__', '__rmod__'); 124 | 125 | Int.cls.$def('__add__', function (self, other) { 126 | return self.add(other); 127 | }, ['other']); 128 | Int.cls.$def_alias('__add__', '__iadd__'); 129 | Int.cls.$def_alias('__add__', '__radd__'); 130 | 131 | Int.cls.$def('__sub__', function (self, other) { 132 | return self.sub(other); 133 | }, ['other']); 134 | Int.cls.$def_alias('__sub__', '__isub__'); 135 | Int.cls.$def_alias('__sub__', '__rsub__'); 136 | 137 | Int.cls.$def('__lshift__', function (self, other) { 138 | return self.lshift(other); 139 | }, ['other']); 140 | Int.cls.$def_alias('__lshift__', '__ilshift__'); 141 | Int.cls.$def_alias('__lshift__', '__rlshift__'); 142 | 143 | Int.cls.$def('__rshift__', function (self, other) { 144 | return self.rshift(other); 145 | }, ['other']); 146 | Int.cls.$def_alias('__rshift__', '__irshift__'); 147 | Int.cls.$def_alias('__rshift__', '__rrshift__'); 148 | 149 | Int.cls.$def('__and__', function (self, other) { 150 | return self.and(other); 151 | }, ['other']); 152 | Int.cls.$def_alias('__and__', '__iand__'); 153 | Int.cls.$def_alias('__and__', '__rand__'); 154 | 155 | Int.cls.$def('__xor__', function (self, other) { 156 | return self.xor(other); 157 | }, ['other']); 158 | Int.cls.$def_alias('__xor__', '__ixor__'); 159 | Int.cls.$def_alias('__xor__', '__rxor__'); 160 | 161 | Int.cls.$def('__or__', function (self, other) { 162 | return self.or(other); 163 | }, ['other']); 164 | Int.cls.$def_alias('__or__', '__ior__'); 165 | Int.cls.$def_alias('__or__', '__ror__'); 166 | 167 | 168 | Int.$map('__abs__'); 169 | Int.$map('__hash__'); 170 | -------------------------------------------------------------------------------- /src/runtime/python/iterator.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | Iterator.$map('__iter__'); 18 | Iterator.$map('__next__'); 19 | -------------------------------------------------------------------------------- /src/runtime/python/list.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | List.cls.$def('__repr__', function (self, state, frame) { 17 | while (true) { 18 | switch (state) { 19 | case 0: 20 | List.cls.check(self); 21 | frame.parts = new Array(self.size); 22 | frame.index = 0; 23 | case 1: 24 | if (frame.index < self.size) { 25 | if (self.get(frame.index).call('__repr__')) { 26 | return 2; 27 | } 28 | } else { 29 | state = 3; 30 | break; 31 | } 32 | case 2: 33 | if (!vm.return_value) { 34 | return; 35 | } 36 | frame.parts[frame.index] = vm.return_value.toString(); 37 | frame.index++; 38 | state = 1; 39 | break; 40 | case 3: 41 | return Str.pack('[' + frame.parts.join(', ') + ']'); 42 | } 43 | } 44 | }); 45 | 46 | 47 | List.$map('append', ['item']); 48 | 49 | 50 | List.cls.$def('__getitem__', function (self, index_or_slice) { 51 | List.cls.check(self); 52 | // TODO: do conversion with __index__ and support slice 53 | if (index_or_slice instanceof Slice) { 54 | var start = Int.unpack(index_or_slice.start, 0); 55 | var stop = Int.unpack(index_or_slice.stop, self.value.length); 56 | var step = Int.unpack(index_or_slice.step, 1); 57 | return self.slice(start, stop, step); 58 | } else { 59 | return self.get(Int.unpack(index_or_slice)); 60 | } 61 | }, ['index_or_slice']); 62 | 63 | List.cls.$def('__setitem__', function (self, index_or_slice, item) { 64 | List.cls.check(self); 65 | return self.set(Int.unpack(index_or_slice), item); 66 | }, ['index_or_slice', 'item']); 67 | 68 | 69 | List.$map('__iter__'); 70 | List.$map('__len__'); 71 | 72 | List.$map('__mul__', ['other']); 73 | -------------------------------------------------------------------------------- /src/runtime/python/method.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | Method.$def('__repr__', function (self, state, frame) { 18 | switch (state) { 19 | case 0: 20 | if (self.self.call('__repr__')) { 21 | return 1; 22 | } 23 | case 1: 24 | if (vm.return_value) { 25 | return new Str(''); 26 | } 27 | } 28 | }); 29 | 30 | py_classmethod.$def('__init__', function (self, func) { 31 | self.setattr('__func__', func); 32 | }, ['func']); 33 | 34 | py_classmethod.$def('__get__', function (self, instance, owner) { 35 | return new Method(self.getattr('__func__'), owner); 36 | }, ['instance', 'owner']); -------------------------------------------------------------------------------- /src/runtime/python/module.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | function PyModule(namespace) { 17 | PyObject.call(this, py_module, namespace); 18 | } 19 | 20 | PyModule.prototype = new PyObject; 21 | -------------------------------------------------------------------------------- /src/runtime/python/none.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | var NONE_STR = Str.pack('None'); 17 | 18 | None.__class__.$def('__new__', function (cls) { 19 | return None; 20 | }); 21 | 22 | None.__class__.$def('__repr__', function (self) { 23 | return NONE_STR; 24 | }); 25 | 26 | None.__class__.$def('__bool__', function (self) { 27 | return False; 28 | }); 29 | -------------------------------------------------------------------------------- /src/runtime/python/object.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | py_object.$def('__new__', function (cls, args, kwargs) { 17 | if (!(cls instanceof PyType)) { 18 | raise(TypeError, 'object.__new__(X): X is not a type object'); 19 | } 20 | if (cls.native !== py_object) { 21 | raise(TypeError, 'object.__new__() is not safe, use ' + cls.native.name + '.__new__()'); 22 | } 23 | return new PyObject(cls, {}); 24 | }, ['*args', '**kwargs']); 25 | 26 | py_object.$def('__getattribute__', function (self, name, state, frame) { 27 | var value; 28 | switch (state) { 29 | case 0: 30 | name = Str.unpack(name); 31 | value = self.__dict__ ? self.getattr(name) : null; 32 | if (!value) { 33 | value = self.__class__.lookup(name); 34 | if (value) { 35 | if (value.call('__get__', [self, self.__class__])) { 36 | return 1; 37 | } 38 | } else { 39 | raise(AttributeError, '\'' + self.__class__.name + '\' object has no attribute \'' + name + '\''); 40 | } 41 | } else { 42 | return value; 43 | } 44 | case 1: 45 | if (except(MethodNotFoundError)) { 46 | return value; 47 | } else if (vm.return_value) { 48 | return vm.return_value 49 | } else { 50 | return null; 51 | } 52 | } 53 | }, ['name']); 54 | 55 | py_object.$def('__setattr__', function (self, name, item, state, frame) { 56 | var descriptor; 57 | switch (state) { 58 | case 0: 59 | descriptor = self.__class__.lookup(name); 60 | if (descriptor && descriptor.__class__.lookup('__set__')) { 61 | if (descriptor.call('__set__', [self, item])) { 62 | return 1; 63 | } 64 | } else { 65 | self.setattr(name, item); 66 | return null; 67 | } 68 | case 1: 69 | return null; 70 | } 71 | }, ['name', 'item']); 72 | 73 | py_object.$def('__repr__', function (self) { 74 | var module = self.__class__.getattr('__module__'); 75 | if (module instanceof Str) { 76 | return Str.pack('<' + module.value + '.' + self.__class__.name + ' object at 0x' + self.get_address() + '>'); 77 | } else { 78 | return Str.pack('<' + self.__class__.name + ' object at 0x' + self.get_address() + '>'); 79 | } 80 | }); 81 | 82 | py_object.$def('__str__', function (self, state, frame) { 83 | switch (state) { 84 | case 0: 85 | if (self.call('__repr__')) { 86 | return 1; 87 | } 88 | case 1: 89 | return vm.return_value; 90 | } 91 | }); 92 | 93 | py_object.$def('__hash__', function (self) { 94 | return Str.pack('object: ' + self.get_address()); 95 | }); 96 | 97 | py_object.$def('__eq__', function (self, other) { 98 | return self === other ? True : False; 99 | }, ['other']); 100 | 101 | py_object.$def_property('__class__', function (self) { 102 | return self.__class__; 103 | }, function (self, value) { 104 | if (!(value instanceof PyType) || value.native != py_object) { 105 | raise(TypeError, 'invalid type of \'value\' argument'); 106 | } 107 | if (self instanceof PyType || self.__class__.native != py_object) { 108 | raise(TypeError, 'object does not support class assignment'); 109 | } 110 | self.__class__ = value; 111 | }); 112 | -------------------------------------------------------------------------------- /src/runtime/python/property.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | Property.cls.$def('__get__', function (self, instance, owner, state, frame) { 17 | switch (state) { 18 | case 0: 19 | if (call(self.getattr('fget'), [instance])) { 20 | return 1; 21 | } 22 | case 1: 23 | return vm.return_value; 24 | } 25 | }, ['instance', 'owner']); 26 | 27 | Property.cls.$def('__set__', function (self, instance, value, state, frame) { 28 | switch (state) { 29 | case 0: 30 | if (call(self.getattr('fset'), [instance, value])) { 31 | return 1; 32 | } 33 | case 1: 34 | break; 35 | } 36 | }, ['instance', 'value']); -------------------------------------------------------------------------------- /src/runtime/python/slice.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | Slice.cls.$def('__repr__', function (self, state, frame) { 17 | switch (state) { 18 | case 0: 19 | Slice.cls.check(self); 20 | frame.result = 'slice('; 21 | if (self.start.call('__repr__')) { 22 | return 1; 23 | } 24 | case 1: 25 | if (!vm.return_value) { 26 | return; 27 | } 28 | frame.result += Str.unpack(vm.return_value) + ', '; 29 | if (self.stop.call('__repr__')) { 30 | return 2; 31 | } 32 | case 2: 33 | if (!vm.return_value) { 34 | return; 35 | } 36 | frame.result += Str.unpack(vm.return_value) + ', '; 37 | if (self.step.call('__repr__')) { 38 | return 3; 39 | } 40 | case 3: 41 | if (!vm.return_value) { 42 | return; 43 | } 44 | frame.result += Str.unpack(vm.return_value) + ')'; 45 | return Str.pack(frame.result); 46 | } 47 | }); 48 | -------------------------------------------------------------------------------- /src/runtime/python/str.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | Str.$def('__new__', function (cls, initializer, state, frame) { 18 | switch (state) { 19 | case 0: 20 | if (!issubclass(cls, Str.cls)) { 21 | raise(TypeError, 'class is not an subclass of str'); 22 | } 23 | if (initializer instanceof Str) { 24 | if (initializer.__class__ == cls) { 25 | return initializer; 26 | } else { 27 | return Str.pack(initializer.value, cls); 28 | } 29 | } 30 | if (initializer.call('__str__')) { 31 | return 1; 32 | } 33 | case 1: 34 | if (except(MethodNotFoundError)) { 35 | raise(TypeError, 'invalid type of str initializer'); 36 | } else if (vm.return_value) { 37 | return vm.return_value; 38 | } 39 | break; 40 | } 41 | }, ['initializer']); 42 | 43 | Str.$def('__str__', function (self) { 44 | return self; 45 | }); 46 | 47 | 48 | 49 | Str.$def('__len__', function (self) { 50 | Str.cls.check(self); 51 | return Int.pack(self.value.length); 52 | }); 53 | 54 | 55 | 56 | 57 | 58 | Str.cls.$def_alias('__add__', '__iadd__'); 59 | Str.cls.$def_alias('__add__', '__radd__'); 60 | 61 | 62 | Str.$map('__repr__'); 63 | Str.$map('__hash__'); 64 | 65 | Str.$def('__eq__', function (self, other) { 66 | return pack_bool(self.eq(other)); 67 | }, ['other']); 68 | 69 | Str.$def('__ne__', function (self, other) { 70 | return pack_bool(!self.eq(other)); 71 | }, ['other']); 72 | 73 | Str.$def('__ge__', function (self, other) { 74 | return pack_bool(self.ge(other)); 75 | }, ['other']); 76 | 77 | Str.$def('__gt__', function (self, other) { 78 | return pack_bool(self.gt(other)); 79 | }, ['other']); 80 | 81 | Str.$def('__le__', function (self, other) { 82 | return pack_bool(self.le(other)); 83 | }, ['other']); 84 | 85 | Str.$def('__lt__', function (self, other) { 86 | return pack_bool(self.lt(other)); 87 | }, ['other']); 88 | 89 | Str.$map('__getitem__', ['index']); 90 | 91 | Str.$map('__add__', ['other']); 92 | 93 | Str.$map('capitalize'); 94 | Str.$map('casefold'); 95 | Str.$map('center', ['width', 'fillchar'], {defaults: {'fillchar': None}}); 96 | Str.$map('count', ['sub', 'start', 'end'], {defaults: {'start': None, 'end': None}}); 97 | Str.$map('encode'); 98 | Str.$map('endswith', ['suffix', 'start', 'end'], {defaults: {'start': None, 'end': None}}); 99 | Str.$map('expandtabs', ['tabsize'], {defaults: {'tabsize': new Int(8)}}); 100 | Str.$map('find', ['sub', 'start', 'end'], {defaults: {'start': None, 'end': None}}); 101 | Str.$map('index', ['sub', 'start', 'end'], {defaults: {'start': None, 'end': None}}); 102 | Str.$map('isalnum'); 103 | Str.$map('isalpha'); 104 | Str.$map('isdecimal'); 105 | Str.$map('isdigit'); 106 | Str.$map('isidentifier'); 107 | Str.$map('islower'); 108 | Str.$map('isnumeric'); 109 | Str.$map('isprintable'); 110 | Str.$map('isspace'); 111 | Str.$map('istitle'); 112 | Str.$map('isupper'); 113 | Str.$map('ljust', ['width', 'fillchar'], {defaults: {'fillchar': None}}); 114 | Str.$map('lower'); 115 | Str.$map('lstrip', ['chars'], {defaults: {'chars': None}}); 116 | Str.$map('partition', ['sep']); 117 | Str.$map('replace', ['old', 'new', 'count'], {defaults: {'count': new Int(-1)}}); 118 | Str.$map('rfind', ['sub', 'start', 'end'], {defaults: {'start': None, 'end': None}}); 119 | Str.$map('rindex', ['sub', 'start', 'end'], {defaults: {'start': None, 'end': None}}); 120 | Str.$map('rjust', ['width', 'fillchar'], {defaults: {'fillchar': None}}); 121 | Str.$map('rpartition', ['sep']); 122 | Str.$map('rsplit', ['sep', 'maxsplit'], {defaults: {'sep': None, 'maxsplit': new Int(-1)}}); 123 | Str.$map('rstrip', ['chars'], {defaults: {'chars': None}}); 124 | Str.$map('split', ['sep', 'maxsplit'], {defaults: {'sep': None, 'maxsplit': new Int(-1)}}); 125 | Str.$map('splitlines', ['keepends'], {defaults: {'keepends': False}}); 126 | Str.$map('startswith', ['prefix', 'start', 'end'], {defaults: {'start': None, 'end': None}}); 127 | Str.$map('strip', ['chars'], {defaults: {'chars': None}}); 128 | Str.$map('swapcase'); 129 | Str.$map('title'); 130 | Str.$map('translate', ['table']); 131 | Str.$map('upper'); 132 | Str.$map('zfill'); 133 | -------------------------------------------------------------------------------- /src/runtime/python/tuple.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | Tuple.$def('__new__', function (cls, initializer, state, frame) { 17 | switch (state) { 18 | case 0: 19 | if (initializer === None) { 20 | return Tuple.EMPTY; 21 | } 22 | if (call(unpack_sequence, [initializer])) { 23 | return 1; 24 | } 25 | case 1: 26 | if (vm.return_value) { 27 | return vm.return_value; 28 | } 29 | } 30 | }, ['initializer'], {defaults: {initializer: None}}); 31 | 32 | Tuple.$def('__repr__', function (self, state, frame) { 33 | while (true) { 34 | switch (state) { 35 | case 0: 36 | Tuple.check(self); 37 | frame.parts = new Array(self.array.length); 38 | frame.index = 0; 39 | case 1: 40 | if (frame.index < self.array.length) { 41 | if (self.array[frame.index].call('__repr__')) { 42 | return 2; 43 | } 44 | } else { 45 | state = 3; 46 | break; 47 | } 48 | case 2: 49 | if (!vm.return_value) { 50 | return; 51 | } 52 | frame.parts[frame.index] = vm.return_value.toString(); 53 | frame.index++; 54 | state = 1; 55 | break; 56 | case 3: 57 | return Str.pack('(' + frame.parts.join(', ') + ')'); 58 | } 59 | } 60 | }); 61 | 62 | Tuple.$map('__iter__'); 63 | -------------------------------------------------------------------------------- /src/runtime/python/type.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | py_type.$def_classmethod('__prepare__', function (mcs, bases) { 17 | return new Dict(); 18 | }, ['bases']); 19 | 20 | py_type.$def('__new__', function (mcs, name, bases, attributes) { 21 | if (!(mcs instanceof PyType)) { 22 | raise(TypeError, 'invalid type of \'mcs\' argument'); 23 | } 24 | if (!(attributes instanceof Dict)) { 25 | raise(TypeError, 'invalid type of \'attributes\' argument'); 26 | } 27 | return new PyType(Str.unpack(name), unpack_tuple(bases), attributes, mcs); 28 | }, ['name', 'bases', 'attributes']); 29 | 30 | py_type.$def('__call__', function (cls, args, kwargs, state, frame) { 31 | switch (state) { 32 | case 0: 33 | if (cls.call_classmethod('__new__', Tuple.unpack(args), kwargs)) { 34 | return 1; 35 | } 36 | case 1: 37 | if (!vm.return_value) { 38 | return null; 39 | } 40 | frame.instance = vm.return_value; 41 | if (vm.return_value.__class__.lookup('__init__')) { 42 | if (vm.return_value.call('__init__', args, kwargs)) { 43 | return 2; 44 | } 45 | } 46 | case 2: 47 | if (vm.return_value) { 48 | return frame.instance; 49 | } 50 | } 51 | }, ['*args', '**kwargs']); 52 | 53 | py_type.$def('__str__', function (cls) { 54 | var module = cls.getattr('__module__'); 55 | if (!(cls instanceof PyType)) { 56 | raise(TypeError, 'invalid type of \'cls\' argument'); 57 | } 58 | if (module instanceof Str) { 59 | return Str.pack(''); 60 | } else { 61 | return Str.pack(''); 62 | } 63 | }); 64 | 65 | py_type.$def_property('__name__', function (cls) { 66 | py_type.check(cls); 67 | return Str.pack(cls.name); 68 | }, function (cls, value) { 69 | py_type.check(cls); 70 | cls.name = Str.unpack(value); 71 | }); 72 | 73 | py_type.$def_property('__mro__', function (cls) { 74 | py_type.check(cls); 75 | return pack_tuple(cls.mro); 76 | }); -------------------------------------------------------------------------------- /src/runtime/python/wrapper.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | py_js_object.$def('__getattr__', function (self, name) { 18 | var value; 19 | py_js_object.check(self); 20 | name = Str.unpack(name); 21 | if (name in self.object) { 22 | value = self.object[name]; 23 | if (typeof value == 'function') { 24 | value = value.bind(self.object); 25 | } 26 | return pack(value); 27 | } 28 | raise(AttributeError, '\'' + self.__class__.name + '\' object has no attribute \'' + name + '\''); 29 | }, ['name']); 30 | 31 | py_js_object.$def('__setattr__', function (self, name, value) { 32 | py_js_object.check(self); 33 | self.object[Str.unpack(name)] = unpack(value); 34 | }, ['name', 'value']); 35 | 36 | 37 | py_js_function.$def('__call__', function (self, args) { 38 | py_js_function.check(self); 39 | return pack(self.func.apply(null, args.map(unpack))); 40 | }, ['*args']); 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/runtime/sys.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | jaspy.module('sys', function ($, module, builtins) { 17 | var VersionInfo = module.$class('_VersionInfo', [builtins.tuple]); 18 | 19 | VersionInfo.$def('__new__', function (cls, major, minor, micro, releaselevel, serial) { 20 | VersionInfo.check_subclass(cls); 21 | return new $.Tuple([major, minor, micro, releaselevel, serial], cls); 22 | }, ['major', 'minor', 'patch', 'releaselevel', 'serial']); 23 | 24 | VersionInfo.$def('__str__', function (self) { 25 | VersionInfo.check(self); 26 | return $.Str.pack('version_info(' + 27 | 'major=' + self.array[0] + ', ' + 28 | 'minor=' + self.array[1] + ', ' + 29 | 'micro=' + self.array[2] + ', ' + 30 | 'releaselevel=' + self.array[3].repr() + ', ' + 31 | 'serial=' + self.array[4] + ')'); 32 | }); 33 | 34 | VersionInfo.$def_property('major', function (self) { 35 | VersionInfo.check(self); 36 | return self.array[0]; 37 | }); 38 | 39 | module.$set('byteorder', $.Str.pack('big')); 40 | 41 | module.$set('copyright', $.Str.pack('Copyright (C) 2016, Maximilian Koehl')); 42 | 43 | module.$set('platform', $.Str.pack('web')); 44 | 45 | module.$set('implementation', $.Str.pack('jaspy')); 46 | 47 | module.$set('maxunicode', $.Int.pack(0xFFFF)); 48 | 49 | module.$set('version', $.Str.pack('3.5.1')); 50 | module.$set('version_info', new $.Tuple([$.Int.pack(3), $.Int.pack(5), $.Int.pack(0), $.Str.pack('dev'), $.Str.pack(0)], VersionInfo)); 51 | 52 | module.$set('jaspy_version', $.Str.pack('/* {{metadata.__version__}} */')); 53 | module.$set('jaspy_version_info', new $.Tuple([ 54 | $.Int.pack(/* {{metadata.__version_info__[0]}} */), 55 | $.Int.pack(/* {{metadata.__version_info__[1]}} */), 56 | $.Int.pack(/* {{metadata.__version_info__[2]}} */), 57 | $.Str.pack('/* {{metadata.__version_info__[3]}} */'), 58 | $.Int.pack(/* {{metadata.__version_info__[4]}} */)], VersionInfo) 59 | ); 60 | 61 | module.$set('modules', new $.Dict($.modules)); 62 | }, ['builtins']); -------------------------------------------------------------------------------- /src/runtime/type.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016, Maximilian Koehl 3 | * 4 | * This program is free software: you can redistribute it and/or modify it under 5 | * the terms of the GNU Lesser General Public License version 3 as published by 6 | * the Free Software Foundation. 7 | * 8 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY 9 | * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 10 | * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU Lesser General Public License along 13 | * with this program. If not, see . 14 | */ 15 | 16 | 17 | function get_mro(cls) { 18 | return cls.mro; 19 | } 20 | 21 | function linearize(cls) { 22 | var pending = cls.bases.map(get_mro), result = [cls]; 23 | var index, head, good; 24 | while (pending.length != 0) { 25 | for (index = 0; index < pending.length; index++) { 26 | head = pending[index][0]; 27 | good = true; 28 | pending.forEach(function (base_mro) { 29 | base_mro.slice(1, base_mro.length).forEach(function (base_cls) { 30 | good &= base_cls != head; 31 | }) 32 | }); 33 | if (good) { 34 | result.push(head); 35 | break; 36 | } 37 | } 38 | if (!good) { 39 | raise(TypeError, 'unable to linearize class hierarchy'); 40 | } 41 | for (index = 0; index < pending.length; index++) { 42 | pending[index] = pending[index].filter(function (base_cls) { 43 | return base_cls != head; 44 | }) 45 | } 46 | pending = pending.filter(function (base_mro) { 47 | return base_mro.length > 0; 48 | }); 49 | } 50 | return result; 51 | } 52 | 53 | 54 | var PyType = PyObject.extend({ 55 | constructor: function (name, bases, attributes, mcs) { 56 | var index, native; 57 | PyObject.call(this, mcs || py_type, attributes || {}); 58 | this.name = name; 59 | this.bases = bases || [py_object]; 60 | this.mro = linearize(this); 61 | this.native = null; 62 | for (index = 0; index < this.mro.length; index++) { 63 | native = this.mro[index].native; 64 | if (native === py_object) { 65 | continue; 66 | } 67 | if (this.native && this.native !== native && native) { 68 | raise(TypeError, 'invalid native type hierarchy'); 69 | } 70 | this.native = native; 71 | } 72 | this.native = this.native || py_object; 73 | }, 74 | 75 | lookup: function (name) { 76 | var index, value; 77 | for (index = 0; index < this.mro.length; index++) { 78 | value = this.mro[index].getattr(name); 79 | if (value) { 80 | return value; 81 | } 82 | } 83 | }, 84 | 85 | define: function (name, item) { 86 | return this.__dict__[name] = item; 87 | }, 88 | 89 | $def: function (name, func, signature, options) { 90 | options = options || {}; 91 | options.name = options.name || name; 92 | options.qualname = options.qualname || (this.name + '.' + options.name); 93 | return this.define(name, $def(func, ['self'].concat(signature || []), options)); 94 | }, 95 | 96 | $def_property: function (name, getter, setter) { 97 | var options = {name: name, qualname: this.name + '.' + name}; 98 | if (getter) { 99 | getter = $def(getter, ['self'], options); 100 | } 101 | if (setter) { 102 | setter = $def(setter, ['self', 'value'], options); 103 | } 104 | return this.define(name, new_property(getter, setter)); 105 | }, 106 | 107 | $def_classmethod: function (name, func, signature, options) { 108 | options = options || {}; 109 | options.name = options.name || name; 110 | options.qualname = options.qualname || (this.name + '.' + options.name); 111 | return this.define(name, $def(func, ['cls'].concat(signature || []), options)); 112 | }, 113 | 114 | $def_alias: function (name, alias) { 115 | return this.define(alias, this.lookup(name)); 116 | }, 117 | 118 | call_classmethod: function (name, args, kwargs) { 119 | var method = this.lookup(name); 120 | if (method) { 121 | return call(method, [this].concat(args || []), kwargs); 122 | } else { 123 | vm.return_value = null; 124 | vm.last_exception = METHOD_NOT_FOUND; 125 | return false; 126 | } 127 | }, 128 | 129 | call_staticmethod: function (name, args, kwargs) { 130 | var method = this.lookup(name); 131 | if (method) { 132 | return call(method, args, kwargs); 133 | } else { 134 | vm.return_value = null; 135 | vm.last_exception = METHOD_NOT_FOUND; 136 | return false; 137 | } 138 | }, 139 | 140 | check: function (object) { 141 | if (!isinstance(object, this)) { 142 | raise(TypeError, 'native type check failed'); 143 | } 144 | }, 145 | 146 | check_subclass: function (cls) { 147 | if (!issubclass(cls, this)) { 148 | raise(TypeError, 'native subclass check failed'); 149 | } 150 | }, 151 | 152 | make: function (dict) { 153 | return new PyObject(this, dict) 154 | } 155 | }); 156 | 157 | 158 | 159 | PyType.native = function (name, bases, attributes, mcs) { 160 | var type = new PyType(name, bases, attributes, mcs); 161 | type.native = type; 162 | return type; 163 | }; 164 | 165 | function $class(name, bases, attributes, mcs) { 166 | return new PyType.native(name, bases, attributes, mcs); 167 | } 168 | 169 | 170 | function issubclass(cls, superclass) { 171 | var index; 172 | if (!(cls instanceof PyType)) { 173 | raise(TypeError, 'first argument must be a class') 174 | } 175 | for (index = 0; index < cls.mro.length; index++) { 176 | if (cls.mro[index] === superclass) { 177 | return true; 178 | } 179 | } 180 | return false; 181 | } 182 | 183 | 184 | $.PyType = PyType; 185 | 186 | $.$class = $class; 187 | 188 | $.issubclass = issubclass; 189 | --------------------------------------------------------------------------------