├── VERSION ├── MANIFEST.in ├── cairo_kernel ├── __init__.py ├── __main__.py ├── syntax_highlighting.py ├── install.py ├── kernel.py └── repl.py ├── CHANGELOG.md ├── .gitignore ├── release.sh ├── tests ├── test_kernel_install.py └── repl_test.py ├── .github └── workflows │ └── test.yml ├── LICENSE.txt ├── README.md ├── setup.py └── notebooks ├── cairo_check_comments_work.ipynb ├── cairo_test_functions.ipynb ├── Cairo test multiple commands in one cell.ipynb ├── Cairo example.ipynb └── Array sum tutorial.ipynb /VERSION: -------------------------------------------------------------------------------- 1 | 0.0.7 -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include VERSION -------------------------------------------------------------------------------- /cairo_kernel/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 0.0.7 2 | 3 | * Bumped up cairo-lang version number to 0.7.1 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **venv 2 | **__pycache__ 3 | **.ipynb_checkpoints 4 | *.egg-info 5 | **dist 6 | **build 7 | MANIFEST -------------------------------------------------------------------------------- /cairo_kernel/__main__.py: -------------------------------------------------------------------------------- 1 | from ipykernel.kernelapp import IPKernelApp 2 | from .kernel import CairoKernel 3 | 4 | IPKernelApp.launch_instance(kernel_class=CairoKernel) -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | # git commands 2 | git checkout master 3 | git pull 4 | git stash 5 | 6 | # tag commands 7 | git tag `cat VERSION` 8 | git push origin --tags 9 | 10 | # upload the library to pypi 11 | python setup.py sdist 12 | twine upload dist/* -------------------------------------------------------------------------------- /tests/test_kernel_install.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | try: 3 | from jupyter_client.kernelspec import find_kernel_specs 4 | except ImportError: 5 | try: 6 | from IPython.kernel.kernelspec import find_kernel_specs 7 | except ImportError: 8 | print("Please install either Jupyter to IPython before continuing") 9 | 10 | def test_cairo_kernelspec(): 11 | print(find_kernel_specs()['cairo']) 12 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test Package 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - uses: actions/setup-python@v2 11 | with: 12 | python-version: 3.8.10 13 | - name: Run pytest 14 | run: | 15 | pip install -U setuptools 16 | pip install -U wheel 17 | pip install . 18 | pytest -------------------------------------------------------------------------------- /tests/repl_test.py: -------------------------------------------------------------------------------- 1 | from cairo_kernel.repl import Repl 2 | import pytest 3 | 4 | my_repl = Repl() 5 | 6 | func = """ 7 | func add2(x : felt) -> (x : felt): 8 | return (x=x+2) 9 | end 10 | """ 11 | 12 | multiple_imports = """ 13 | from starkware.cairo.common.uint256 import ( 14 | Uint256, uint256_add, uint256_sub, uint256_lt, uint256_check 15 | ) 16 | """ 17 | 18 | import_struct = """ 19 | struct Location: 20 | member row : felt 21 | member col : felt 22 | end""" 23 | 24 | 25 | @pytest.mark.parametrize("func", [func, multiple_imports, import_struct]) 26 | def test_repl_run(func): 27 | my_repl.run(func) -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | Copyright (c) 2020 Ankit CHiPLUNKAR 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | The above copyright notice and this permission notice shall be included in all 10 | copies or substantial portions of the Software. 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. -------------------------------------------------------------------------------- /cairo_kernel/syntax_highlighting.py: -------------------------------------------------------------------------------- 1 | from pygments import token 2 | from pygments.lexer import RegexLexer, words 3 | 4 | KEYWORDS = [ 5 | "func", 6 | "struct", 7 | "namespace", 8 | "end", 9 | "call", 10 | "ret", 11 | "jmp", 12 | "if", 13 | "let", 14 | "const", 15 | "import", 16 | "from", 17 | "as", 18 | "abs", 19 | "rel", 20 | "static_assert", 21 | "local", 22 | "tempvar", 23 | "felt", 24 | "return", 25 | "assert", 26 | "member", 27 | "cast", 28 | "else", 29 | "alloc_locals", 30 | "with", 31 | "with_attr", 32 | "nondet", 33 | ] 34 | 35 | 36 | class CairoLexer(RegexLexer): 37 | name = "cairo" 38 | 39 | tokens = { 40 | "root": [ 41 | (words(KEYWORDS, prefix=r"\b", suffix=r"\b"), token.Keyword), 42 | (words(("SIZEOF_LOCALS", "SIZE"), prefix=r"\b", suffix=r"\b"), token.Literal), 43 | (r"%builtins|%lang", token.Keyword), 44 | (words(("ap", "fp"), prefix=r"\b", suffix=r"\b"), token.Name.Entity), 45 | (r"!=|->", token.Operator), 46 | (r"[+\-*/&]", token.Operator), 47 | (r"[:;,.=\[\]\(\)\{\}]", token.Punctuation), 48 | (r"-?[0-9]+", token.Number), 49 | (r"[a-zA-Z_][a-zA-Z_0-9]*", token.Text), 50 | (r"#.*", token.Comment), 51 | (r"%\{(.|\n)*?%\}", token.Text), 52 | (r"%\[(.|\n)*?%\]", token.Text), 53 | (r"@\w+", token.Keyword), 54 | (r"<[a-zA-Z0-9 _\-]+>", token.Comment), 55 | (r" ", token.Text), 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /cairo_kernel/install.py: -------------------------------------------------------------------------------- 1 | """Script that installs the kernel""" 2 | 3 | import os 4 | import sys 5 | import json 6 | import getopt 7 | from shutil import copyfile 8 | 9 | try: 10 | from jupyter_client.kernelspec import install_kernel_spec 11 | except ImportError: 12 | try: 13 | from IPython.kernel.kernelspec import install_kernel_spec 14 | except ImportError: 15 | print("Please install either Jupyter to IPython before continuing") 16 | from IPython.utils.tempdir import TemporaryDirectory 17 | 18 | kernel_json = { 19 | "argv": [sys.executable, 20 | "-m", "cairo_kernel", "-f", "{connection_file}"], 21 | "display_name": "Cairo", 22 | "language": "text", 23 | "name": "Cairo", 24 | } 25 | 26 | 27 | def install_my_kernel_spec(user=True, prefix=None): 28 | user = '--user' in sys.argv or not _is_root() 29 | with TemporaryDirectory() as td: 30 | os.chmod(td, 0o755) # Starts off as 700, not user readable 31 | 32 | with open(os.path.join(td, 'kernel.json'), 'w') as f: 33 | json.dump(kernel_json, f, sort_keys=True) 34 | 35 | try: 36 | install_kernel_spec(td, kernel_json['name'], user=user, 37 | replace=True, prefix=prefix) 38 | except OSError: 39 | install_kernel_spec(td, kernel_json['name'], user=not user, 40 | replace=True, prefix=prefix) 41 | 42 | def main(argv=()): 43 | prefix = None 44 | user = not _is_root() 45 | 46 | opts, _ = getopt.getopt(argv[1:], '', ['user', 'prefix=']) 47 | for k, v in opts: 48 | if k == '--user': 49 | user = True 50 | elif k == '--prefix': 51 | prefix = v 52 | user = False 53 | 54 | install_my_kernel_spec(user=user, prefix=prefix) 55 | 56 | 57 | def _is_root(): 58 | try: 59 | return os.geteuid() == 0 60 | except AttributeError: 61 | return False # assume not an admin on non-Unix platforms 62 | 63 | 64 | if __name__ == '__main__': 65 | main(argv=sys.argv) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | cairo_jupyter 2 | =========== 3 | 4 | ``cairo_jupyter`` hosts notebooks and code for cairo-kernel its a simple Jupyter kernel for Cairo a smart contract programing language for STARKs. 5 | 6 | Hosted on binder: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/ankitchiplunkar/cairo-jupyter/HEAD) 7 | 8 | Project managenemt: [Kanban](https://ankitchiplunkar.notion.site/Cairo-kernel-starkware-py-5f64ee70cfde4578969c430bf1a14531) 9 | 10 | Installation 11 | ------------ 12 | 1. Install virtualenv 13 | 14 | ``` 15 | virtualenv -p python3.8 venv 16 | source venv/bin/activate 17 | ``` 18 | 19 | 2. To install ``cairo_kernel``: 20 | 21 | ``` 22 | # Upgrade to latest version of setuptools and wheel 23 | pip install -U setuptools 24 | pip install -U wheel 25 | pip install cairo-kernel 26 | ``` 27 | 28 | Using the Kernel 29 | --------------------- 30 | 31 | [Cairo example](https://github.com/ankitchiplunkar/cairo-jupyter/blob/master/notebooks/Cairo%20example.ipynb) is a working example of the notebook. 32 | 33 | 34 | **Notebook**: The *New* menu in the notebook should show an option for an Cairo notebook. 35 | 36 | **Console frontends**: To use it with the console frontends, add ``--kernel cairo`` to 37 | their command line arguments. 38 | 39 | Running the kernel on notebook 40 | -------------------------------- 41 | 42 | 1. Start the notebook 43 | 44 | ``` 45 | jupyter notebook 46 | ``` 47 | 48 | 2. Start cairo kernel via UI 49 | 50 | ![cairo_jupyter_gif](https://user-images.githubusercontent.com/5904910/146619802-6ee1bb5d-243a-4e0d-9ab2-064e101f5bcd.gif) 51 | 52 | 53 | 54 | Development 55 | ------------------------------- 56 | 57 | 1. Clone & enter the repo. 58 | 59 | ``` 60 | git clone https://github.com/ankitchiplunkar/cairo-jupyter.git 61 | ``` 62 | 63 | 2. Install required libraries. 64 | 65 | ``` 66 | pip install -r requirements.txt 67 | ``` 68 | 69 | 3. Install the cairo-jupyter library locally: 70 | ``` 71 | pip install -e . 72 | python -m cairo_kernel.install 73 | ``` 74 | 75 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import setuptools 4 | from distutils.command.install import install 5 | from os import path 6 | 7 | # managing version 8 | with open("VERSION", 'r') as f: 9 | VERSION = f.read() 10 | 11 | # managing readme 12 | this_directory = path.abspath(path.dirname(__file__)) 13 | with open(path.join(this_directory, 'README.md'), 'r') as f: 14 | readme = f.read() 15 | 16 | class InstallWithKernelspec(install): 17 | def run(self): 18 | # Regular installation 19 | install.run(self) 20 | 21 | # Kernel installation 22 | if "NO_KERNEL_INSTALL" in os.environ: 23 | # If the NO_KERNEL_INSTALL env variable is set then skip the kernel installation. 24 | return 25 | else: 26 | print("Reached kernel install") 27 | from cairo_kernel import install as kernel_install 28 | kernel_install.main(argv=sys.argv) 29 | 30 | setuptools.setup( 31 | name='cairo_kernel', 32 | version=VERSION, 33 | packages=['cairo_kernel'], 34 | cmdclass={'install': InstallWithKernelspec}, 35 | description='Jupyter kernel for Cairo language', 36 | long_description=readme, 37 | long_description_content_type='text/markdown', 38 | author='Ankit Chiplunkar, Arie Benhamou', 39 | author_email='ankitchiplunkar@gmail.com, benhamou.arie@gmail.com', 40 | python_requires='>=3.6, <3.10', 41 | keywords=['ethereum', 'starkware'], 42 | setup_requires=[ 43 | "jupyter_client==6.1.12", 44 | "ipykernel==6.4.2", 45 | ], 46 | install_requires=[ 47 | "pygments==2.11.2", 48 | "contextvars==2.4", 49 | "cairo-lang==0.7.1", 50 | "jupyter==1.0.0", 51 | "jupyter_client==6.1.12", 52 | "ipykernel==6.4.2", 53 | "pytest==6.2.5", 54 | ], 55 | classifiers=[ 56 | 'Development Status :: 3 - Alpha', 57 | 'Intended Audience :: Developers', 58 | 'Natural Language :: English', 59 | 'Programming Language :: Python :: 3', 60 | 'Programming Language :: Python :: 3.6', 61 | ] 62 | ) -------------------------------------------------------------------------------- /notebooks/cairo_check_comments_work.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "d70927cf", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "# These comments should be discarded\n", 11 | "\n", 12 | "# The empty line below used to return an error\n", 13 | "\n", 14 | "tempvar x = 10\n", 15 | "\n", 16 | "# The empty line above used to return an error" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 2, 22 | "id": "d6196e17", 23 | "metadata": {}, 24 | "outputs": [ 25 | { 26 | "name": "stdout", 27 | "output_type": "stream", 28 | "text": [ 29 | "10" 30 | ] 31 | } 32 | ], 33 | "source": [ 34 | "x" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": 3, 40 | "id": "2effbbdd", 41 | "metadata": {}, 42 | "outputs": [], 43 | "source": [ 44 | "from starkware.cairo.common.serialize import serialize_word" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": 4, 50 | "id": "b6e816e6", 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "func main{output_ptr : felt*}():\n", 55 | " #Prints '1234'\n", 56 | " serialize_word(1234)\n", 57 | " #Prints '4321'\n", 58 | " serialize_word(4321)\n", 59 | " return ()\n", 60 | "end" 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": 6, 66 | "id": "a9172102", 67 | "metadata": {}, 68 | "outputs": [ 69 | { 70 | "name": "stdout", 71 | "output_type": "stream", 72 | "text": [ 73 | "32" 74 | ] 75 | } 76 | ], 77 | "source": [ 78 | "# Function still doesn't return the right output, but adding comments inside doesn't make it bug anymore \n", 79 | "main" 80 | ] 81 | } 82 | ], 83 | "metadata": { 84 | "kernelspec": { 85 | "display_name": "Cairo", 86 | "language": "text", 87 | "name": "cairo" 88 | }, 89 | "language_info": { 90 | "file_extension": ".cairo", 91 | "mimetype": "text/plain", 92 | "name": "Cairo" 93 | } 94 | }, 95 | "nbformat": 4, 96 | "nbformat_minor": 5 97 | } 98 | -------------------------------------------------------------------------------- /cairo_kernel/kernel.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import traceback 3 | 4 | from ipykernel.kernelbase import Kernel 5 | from prompt_toolkit.lexers import PygmentsLexer 6 | from .syntax_highlighting import CairoLexer 7 | from starkware.cairo.lang.compiler.error_handling import LocationError 8 | from .repl import Repl 9 | 10 | 11 | class CairoKernel(Kernel): 12 | implementation = "Cairo" 13 | implementation_version = "0.0.1" 14 | language = "cairo" 15 | language_version = "0.4.2" 16 | language_info = { 17 | "name": "Cairo", 18 | "mimetype": "text/plain", 19 | "file_extension": ".cairo", 20 | # TODO(add lexer)" 21 | # "pygments_lexer": PygmentsLexer(CairoLexer), 22 | } 23 | banner = "Cairo Kernel - scale dApps using STARKs" 24 | repl = Repl() 25 | 26 | def do_execute( 27 | self, code, silent, store_history=True, user_expressions=None, allow_stdin=False 28 | ): 29 | if not silent: 30 | try: 31 | value = self.repl.run(code) 32 | stream_content = {"name": "stdout", "text": str(value) if value else value} 33 | self.send_response(self.iopub_socket, "stream", stream_content) 34 | return { 35 | "status": "ok", 36 | # The base class increments the execution count 37 | "execution_count": self.execution_count, 38 | "payload": [], 39 | "user_expressions": {}, 40 | } 41 | 42 | except LocationError as exc: 43 | error_msg = str(exc) 44 | except Exception: 45 | error_msg = str(traceback.print_exc()) 46 | 47 | stream_content = {"name": "stdout", "text": error_msg} 48 | self.send_response(self.iopub_socket, "stream", stream_content) 49 | return { 50 | "status": "error", 51 | #TODO(arie): set right parameteres for status = error 52 | # "ename": "my_ename", 53 | # "evalue": "my_evalue", 54 | # "traceback": error_msg 55 | } 56 | 57 | 58 | 59 | if __name__ == "__main__": 60 | from ipykernel.kernelapp import IPKernelApp 61 | IPKernelApp.launch_instance(kernel_class=CairoKernel) 62 | -------------------------------------------------------------------------------- /notebooks/cairo_test_functions.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "0718c460", 7 | "metadata": { 8 | "scrolled": false 9 | }, 10 | "outputs": [], 11 | "source": [ 12 | "%builtins output" 13 | ] 14 | }, 15 | { 16 | "cell_type": "code", 17 | "execution_count": 2, 18 | "id": "40236556", 19 | "metadata": { 20 | "scrolled": true 21 | }, 22 | "outputs": [], 23 | "source": [ 24 | "func add1(x : felt) -> (x : felt): \n", 25 | " return (x=x+1) \n", 26 | " end" 27 | ] 28 | }, 29 | { 30 | "cell_type": "code", 31 | "execution_count": 3, 32 | "id": "f9b17f12", 33 | "metadata": {}, 34 | "outputs": [], 35 | "source": [ 36 | "let (a) = add1(10)" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 4, 42 | "id": "a0886540", 43 | "metadata": {}, 44 | "outputs": [ 45 | { 46 | "name": "stdout", 47 | "output_type": "stream", 48 | "text": [ 49 | "11" 50 | ] 51 | } 52 | ], 53 | "source": [ 54 | "a" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 5, 60 | "id": "9219009c", 61 | "metadata": {}, 62 | "outputs": [], 63 | "source": [ 64 | "from starkware.cairo.common.serialize import serialize_word" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": null, 70 | "id": "82db1a61", 71 | "metadata": {}, 72 | "outputs": [ 73 | { 74 | "name": "stdout", 75 | "output_type": "stream", 76 | "text": [ 77 | ":1:1: While trying to retrieve the implicit argument 'output_ptr' in:\n", 78 | "serialize_word(1234)\n", 79 | "^******************^\n", 80 | "/home/arie/cairo_venv/lib/python3.8/site-packages/starkware/cairo/common/serialize.cairo:2:21: Unknown identifier 'output_ptr'.\n", 81 | "func serialize_word{output_ptr : felt*}(word):\n", 82 | " ^****************^" 83 | ] 84 | } 85 | ], 86 | "source": [ 87 | "serialize_word(1234)" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": null, 93 | "id": "437ea328", 94 | "metadata": {}, 95 | "outputs": [], 96 | "source": [] 97 | } 98 | ], 99 | "metadata": { 100 | "kernelspec": { 101 | "display_name": "Cairo", 102 | "language": "text", 103 | "name": "cairo" 104 | }, 105 | "language_info": { 106 | "file_extension": ".cairo", 107 | "mimetype": "text/plain", 108 | "name": "Cairo" 109 | } 110 | }, 111 | "nbformat": 4, 112 | "nbformat_minor": 5 113 | } 114 | -------------------------------------------------------------------------------- /notebooks/Cairo test multiple commands in one cell.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "3e2ea0b0", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "from starkware.cairo.common.alloc import alloc\n", 11 | "from starkware.cairo.common.serialize import serialize_word" 12 | ] 13 | }, 14 | { 15 | "cell_type": "code", 16 | "execution_count": 2, 17 | "id": "9318d1cd", 18 | "metadata": {}, 19 | "outputs": [ 20 | { 21 | "name": "stdout", 22 | "output_type": "stream", 23 | "text": [ 24 | "2:0" 25 | ] 26 | } 27 | ], 28 | "source": [ 29 | "output_ptr" 30 | ] 31 | }, 32 | { 33 | "cell_type": "code", 34 | "execution_count": null, 35 | "id": "020ef30c", 36 | "metadata": {}, 37 | "outputs": [ 38 | { 39 | "name": "stdout", 40 | "output_type": "stream", 41 | "text": [ 42 | ":1:1: Unknown value for memory cell at address 2:0.\n", 43 | "[output_ptr]\n", 44 | "^**********^" 45 | ] 46 | } 47 | ], 48 | "source": [ 49 | "[output_ptr]" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 4, 55 | "id": "af269190", 56 | "metadata": {}, 57 | "outputs": [], 58 | "source": [ 59 | "func main{output_ptr : felt*}():\n", 60 | " tempvar x = 10\n", 61 | " tempvar y = x + x\n", 62 | " tempvar z = y * y + x\n", 63 | " serialize_word(x)\n", 64 | " serialize_word(y)\n", 65 | " serialize_word(z)\n", 66 | " return ()\n", 67 | "end" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": 5, 73 | "id": "40236556", 74 | "metadata": { 75 | "scrolled": true 76 | }, 77 | "outputs": [], 78 | "source": [ 79 | "func main2{output_ptr : felt*}():\n", 80 | " tempvar x = 10\n", 81 | " tempvar y = x + x\n", 82 | " tempvar z = y * y + x\n", 83 | " serialize_word(x)\n", 84 | " serialize_word(y)\n", 85 | " serialize_word(z)\n", 86 | " return ()\n", 87 | "end\n", 88 | "# This runs properly (see cell below)\n", 89 | "main()" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": 6, 95 | "id": "de52b009", 96 | "metadata": {}, 97 | "outputs": [ 98 | { 99 | "name": "stdout", 100 | "output_type": "stream", 101 | "text": [ 102 | "2:3" 103 | ] 104 | } 105 | ], 106 | "source": [ 107 | "output_ptr" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": null, 113 | "id": "9e57d407", 114 | "metadata": {}, 115 | "outputs": [], 116 | "source": [] 117 | } 118 | ], 119 | "metadata": { 120 | "kernelspec": { 121 | "display_name": "Cairo", 122 | "language": "text", 123 | "name": "cairo" 124 | }, 125 | "language_info": { 126 | "file_extension": ".cairo", 127 | "mimetype": "text/plain", 128 | "name": "Cairo" 129 | } 130 | }, 131 | "nbformat": 4, 132 | "nbformat_minor": 5 133 | } 134 | -------------------------------------------------------------------------------- /notebooks/Cairo example.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": null, 6 | "id": "f9a59b66", 7 | "metadata": {}, 8 | "outputs": [ 9 | { 10 | "name": "stdout", 11 | "output_type": "stream", 12 | "text": [ 13 | ":1:1: Redefinition of builtins directive.\n", 14 | "%builtins output\n", 15 | "^**************^" 16 | ] 17 | } 18 | ], 19 | "source": [ 20 | "%builtins output" 21 | ] 22 | }, 23 | { 24 | "cell_type": "code", 25 | "execution_count": 3, 26 | "id": "35d56058", 27 | "metadata": {}, 28 | "outputs": [], 29 | "source": [ 30 | "tempvar x = 10" 31 | ] 32 | }, 33 | { 34 | "cell_type": "code", 35 | "execution_count": 4, 36 | "id": "4bdd7a0c", 37 | "metadata": {}, 38 | "outputs": [], 39 | "source": [ 40 | "tempvar y = x + x" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 5, 46 | "id": "681b35e6", 47 | "metadata": {}, 48 | "outputs": [ 49 | { 50 | "name": "stdout", 51 | "output_type": "stream", 52 | "text": [ 53 | "20" 54 | ] 55 | } 56 | ], 57 | "source": [ 58 | "y" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 6, 64 | "id": "3027c4e9", 65 | "metadata": {}, 66 | "outputs": [], 67 | "source": [ 68 | "tempvar z = x*x + y" 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "execution_count": 7, 74 | "id": "f525a1fb", 75 | "metadata": {}, 76 | "outputs": [ 77 | { 78 | "name": "stdout", 79 | "output_type": "stream", 80 | "text": [ 81 | "120" 82 | ] 83 | } 84 | ], 85 | "source": [ 86 | "z" 87 | ] 88 | }, 89 | { 90 | "cell_type": "code", 91 | "execution_count": null, 92 | "id": "f4a403af", 93 | "metadata": {}, 94 | "outputs": [ 95 | { 96 | "name": "stdout", 97 | "output_type": "stream", 98 | "text": [ 99 | "None" 100 | ] 101 | } 102 | ], 103 | "source": [ 104 | "# test" 105 | ] 106 | }, 107 | { 108 | "cell_type": "code", 109 | "execution_count": 9, 110 | "id": "87fdaf70", 111 | "metadata": {}, 112 | "outputs": [], 113 | "source": [ 114 | "from starkware.cairo.common.serialize import serialize_word" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": 10, 120 | "id": "db884d3a", 121 | "metadata": {}, 122 | "outputs": [], 123 | "source": [ 124 | "serialize_word(5)" 125 | ] 126 | }, 127 | { 128 | "cell_type": "code", 129 | "execution_count": 13, 130 | "id": "d7eeabfa", 131 | "metadata": {}, 132 | "outputs": [], 133 | "source": [ 134 | "func add1{output_ptr : felt*}(x : felt) -> (x : felt): \n", 135 | " return (x=x+1) \n", 136 | " end\n" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 14, 142 | "id": "d04c77bb", 143 | "metadata": {}, 144 | "outputs": [], 145 | "source": [ 146 | "let (a) = add1(10)" 147 | ] 148 | }, 149 | { 150 | "cell_type": "code", 151 | "execution_count": 15, 152 | "id": "297fa6a8", 153 | "metadata": {}, 154 | "outputs": [ 155 | { 156 | "name": "stdout", 157 | "output_type": "stream", 158 | "text": [ 159 | "11" 160 | ] 161 | } 162 | ], 163 | "source": [ 164 | "a" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": null, 170 | "id": "38b7244c", 171 | "metadata": {}, 172 | "outputs": [], 173 | "source": [] 174 | } 175 | ], 176 | "metadata": { 177 | "kernelspec": { 178 | "display_name": "Cairo", 179 | "language": "text", 180 | "name": "cairo" 181 | }, 182 | "language_info": { 183 | "file_extension": ".cairo", 184 | "mimetype": "text/plain", 185 | "name": "Cairo" 186 | } 187 | }, 188 | "nbformat": 4, 189 | "nbformat_minor": 5 190 | } 191 | -------------------------------------------------------------------------------- /notebooks/Array sum tutorial.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "fdc60609", 6 | "metadata": {}, 7 | "source": [ 8 | "# Array sum tutorial\n", 9 | "This is a tutorial in the to demonstrate the functioning of cairo and write a siple recursive function to sum arrays\n", 10 | "\n", 11 | "Cairo is not a high-level language. It’s a low-level language with some powerful syntactic sugar to allow writing maintainable code. The advantage is that the Cairo language allows you to write very efficient code " 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "id": "30da1ac2", 17 | "metadata": {}, 18 | "source": [ 19 | "### The `array_sum` function" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": 1, 25 | "id": "975865bd", 26 | "metadata": {}, 27 | "outputs": [], 28 | "source": [ 29 | "func array_sum(arr : felt*, size) -> (sum):\n", 30 | " if size == 0:\n", 31 | " return (sum=0)\n", 32 | " end\n", 33 | "\n", 34 | " # size is not zero.\n", 35 | " let (sum_of_rest) = array_sum(arr=arr + 1, size=size - 1)\n", 36 | " return (sum=[arr] + sum_of_rest)\n", 37 | "end" 38 | ] 39 | }, 40 | { 41 | "cell_type": "markdown", 42 | "id": "e2bbe167", 43 | "metadata": {}, 44 | "source": [ 45 | "`func array_sum(arr : felt*, size) -> (sum)`: defines a function named array_sum which takes two arguments: arr and size. arr points to an array of size elements. The type of arr is `felt*` which is a pointer (for more information about felt, see below). The function declares that it returns one value called sum. The scope of the function ends with the word end (although end doesn’t mean that the function returns – you must add the return statement explicitly).\n", 46 | "\n", 47 | "You may have noticed that we’ve used recursion in the code above rather than the loop structure you may have expected. The main reason for this is that the Cairo memory is immutable – once you write the value of a memory cell, this cell cannot change in the future. Immutability of cairo is also why, **we cannot rerun the same function again in the kernel**, because cairo will throw a `Redefinition` error." 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "id": "afbf49da", 53 | "metadata": {}, 54 | "source": [ 55 | "### Importing variables\n", 56 | "\n", 57 | "The line `from starkware.cairo.common.serialize import serialize_word` instructs the compiler to compile the file `starkware/cairo/common/serialize.cairo`, and to expose the identifier serialize_word." 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 2, 63 | "id": "d1cb5ae5", 64 | "metadata": {}, 65 | "outputs": [], 66 | "source": [ 67 | "from starkware.cairo.common.serialize import serialize_word" 68 | ] 69 | }, 70 | { 71 | "cell_type": "markdown", 72 | "id": "e53e1897", 73 | "metadata": {}, 74 | "source": [ 75 | "We use the standard library function alloc() to allocate a new memory segment. In practice the exact location of the allocated memory will be determined only when the program terminates, which allows us to avoid specifying the size of the allocation" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": 3, 81 | "id": "2a2e595e", 82 | "metadata": {}, 83 | "outputs": [], 84 | "source": [ 85 | "from starkware.cairo.common.alloc import alloc" 86 | ] 87 | }, 88 | { 89 | "cell_type": "markdown", 90 | "id": "6563d1b7", 91 | "metadata": {}, 92 | "source": [ 93 | "### The `main` function" 94 | ] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "execution_count": 4, 99 | "id": "7b39b494", 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "func main{output_ptr : felt*}():\n", 104 | " const ARRAY_SIZE = 3\n", 105 | "\n", 106 | " # Allocate an array.\n", 107 | " let (ptr) = alloc()\n", 108 | "\n", 109 | " # Populate some values in the array.\n", 110 | " assert [ptr] = 9\n", 111 | " assert [ptr + 1] = 16\n", 112 | " assert [ptr + 2] = 25\n", 113 | "\n", 114 | " # Call array_sum to compute the sum of the elements.\n", 115 | " let (sum) = array_sum(arr=ptr, size=ARRAY_SIZE)\n", 116 | "\n", 117 | " # Write the sum to the program output.\n", 118 | " serialize_word(sum)\n", 119 | "\n", 120 | " return ()\n", 121 | "end" 122 | ] 123 | }, 124 | { 125 | "cell_type": "markdown", 126 | "id": "3cdb6ddb", 127 | "metadata": {}, 128 | "source": [ 129 | "Now, let’s write a `main()` function that will use `array_sum()`. To do this, we will need to allocate space for the array. The syntax `{output_ptr : felt*}` declares an “implicit argument”, which means that behind the scenes, it adds both a corresponding argument and return value. More information about implicit arguments can be found in [here](https://www.cairo-lang.org/docs/how_cairo_works/builtins.html#implicit-arguments).\n", 130 | "\n", 131 | "To write a to the output, we can use the library function `serialize_word(x)`. serialize_word gets one argument (the value we want to write) and one implicit argument output_ptr. It writes x to the memory cell pointed by output_ptr and returns output_ptr + 1. Now the implicit argument mechanism kicks in: in the first call to serialize_word() the Cairo compiler passes the value of output_ptr as the implicit argument. In the second call it uses the value returned by the first call." 132 | ] 133 | }, 134 | { 135 | "cell_type": "markdown", 136 | "id": "dd8bbadf", 137 | "metadata": {}, 138 | "source": [ 139 | "### The `main` function\n", 140 | "\n", 141 | "Running the main function is as simple as caling `main()`" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": 5, 147 | "id": "2ff7bd4c", 148 | "metadata": {}, 149 | "outputs": [], 150 | "source": [ 151 | "main()" 152 | ] 153 | }, 154 | { 155 | "cell_type": "markdown", 156 | "id": "1bc2f368", 157 | "metadata": {}, 158 | "source": [ 159 | "To read the result of `main` function we print the value at second last location of `output_ptr`" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": 6, 165 | "id": "e6f52caa", 166 | "metadata": {}, 167 | "outputs": [ 168 | { 169 | "name": "stdout", 170 | "output_type": "stream", 171 | "text": [ 172 | "2:1" 173 | ] 174 | } 175 | ], 176 | "source": [ 177 | "output_ptr" 178 | ] 179 | }, 180 | { 181 | "cell_type": "code", 182 | "execution_count": 7, 183 | "id": "aae24575", 184 | "metadata": {}, 185 | "outputs": [ 186 | { 187 | "name": "stdout", 188 | "output_type": "stream", 189 | "text": [ 190 | "50" 191 | ] 192 | } 193 | ], 194 | "source": [ 195 | "[output_ptr-1]" 196 | ] 197 | } 198 | ], 199 | "metadata": { 200 | "kernelspec": { 201 | "display_name": "Cairo", 202 | "language": "text", 203 | "name": "cairo" 204 | }, 205 | "language_info": { 206 | "file_extension": ".cairo", 207 | "mimetype": "text/plain", 208 | "name": "Cairo" 209 | } 210 | }, 211 | "nbformat": 4, 212 | "nbformat_minor": 5 213 | } 214 | -------------------------------------------------------------------------------- /cairo_kernel/repl.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from typing import List, Union 3 | 4 | from starkware.cairo.lang.compiler.assembler import assemble 5 | from starkware.cairo.lang.compiler.ast.cairo_types import TypePointer, TypeStruct, TypeFelt 6 | from starkware.cairo.lang.compiler.ast.code_elements import ( 7 | CodeElement, 8 | CodeElementFunction, 9 | CodeElementImport, 10 | CodeElementScoped, 11 | ) 12 | 13 | from starkware.cairo.lang.compiler.ast.expr import Expression 14 | from starkware.cairo.lang.compiler.ast.expr_func_call import ExprFuncCall 15 | from starkware.cairo.lang.compiler.ast.module import CairoModule 16 | from starkware.cairo.lang.compiler.ast.node import AstNode 17 | from starkware.cairo.lang.compiler.cairo_compile import get_module_reader 18 | from starkware.cairo.lang.compiler.constants import MAIN_SCOPE 19 | from starkware.cairo.lang.compiler.error_handling import LocationError 20 | from starkware.cairo.lang.compiler.expression_evaluator import ExpressionEvaluator 21 | from starkware.cairo.lang.compiler.identifier_definition import ( 22 | FutureIdentifierDefinition, 23 | ReferenceDefinition, 24 | ) 25 | from starkware.cairo.lang.compiler.identifier_manager import IdentifierManager 26 | from starkware.cairo.lang.compiler.import_loader import ( 27 | DirectDependenciesCollector, 28 | ImportsCollector, 29 | ) 30 | from starkware.cairo.lang.compiler.instruction import Register 31 | from starkware.cairo.lang.compiler.parser import ParserError, parse 32 | from starkware.cairo.lang.compiler.preprocessor.identifier_collector import IdentifierCollector 33 | from starkware.cairo.lang.compiler.preprocessor.preprocessor import Preprocessor, ReferenceState 34 | from starkware.cairo.lang.compiler.preprocessor.struct_collector import StructCollector 35 | from starkware.cairo.lang.compiler.preprocessor.unique_labels import UniqueLabelCreator 36 | from starkware.cairo.lang.compiler.program import StrippedProgram 37 | from starkware.cairo.lang.compiler.scoped_name import ScopedName 38 | from .syntax_highlighting import CairoLexer 39 | from starkware.cairo.lang.vm.cairo_runner import CairoRunner 40 | 41 | PRIME = 2 ** 251 + 17 * 2 ** 192 + 1 42 | 43 | INPUT_FILENAME = "" 44 | CAIRO_BUILTIN_SCOPE = ScopedName.from_string("starkware.cairo.common.cairo_builtins") 45 | 46 | 47 | class Repl: 48 | def __init__(self): 49 | self.identifiers = IdentifierManager() 50 | self.struct_collector = StructCollector(identifiers=self.identifiers) 51 | builtins = ["output", "pedersen", "range_check", "ecdsa"] 52 | # TODO(lior, 01/10/2021): Get prime as an argument. 53 | self.preprocessor = Preprocessor(prime=PRIME, builtins=builtins, identifiers=self.identifiers) 54 | self.identifier_collector = IdentifierCollector() 55 | self.unique_label_creator = UniqueLabelCreator() 56 | # TODO(lior, 01/10/2021): Get path as an argument. 57 | path = ["src"] 58 | self.module_reader = get_module_reader(path) 59 | 60 | layout = "small" 61 | program = StrippedProgram(prime=PRIME, data=[], builtins=builtins, main=0) 62 | self.runner = CairoRunner(program=program, layout=layout, proof_mode=False) 63 | self.runner.initialize_segments() 64 | self.runner.initialize_main_entrypoint() 65 | self.runner.initialize_vm(hint_locals={}) 66 | 67 | for offset, builtin in enumerate(builtins, -2 - len(builtins)): 68 | builtin_name = MAIN_SCOPE + f"{builtin}_ptr" 69 | self.preprocessor.add_future_definition( 70 | name=builtin_name, 71 | future_definition=FutureIdentifierDefinition(identifier_type=ReferenceDefinition), 72 | ) 73 | if builtin == "output": 74 | self.preprocessor.add_simple_reference( 75 | name=builtin_name, 76 | reg=Register.FP, 77 | cairo_type=TypePointer( 78 | pointee=TypeFelt(), 79 | ), 80 | offset=offset, 81 | location=None, 82 | ) 83 | else: 84 | self.preprocessor.add_simple_reference( 85 | name=builtin_name, 86 | reg=Register.FP, 87 | cairo_type=TypePointer( 88 | pointee=TypeStruct( 89 | scope=CAIRO_BUILTIN_SCOPE + "HashBuiltin", 90 | is_fully_resolved=True, 91 | ), 92 | ), 93 | offset=offset, 94 | location=None, 95 | ) 96 | self.preprocessor.reference_states[builtin_name] = ReferenceState.ALLOW_IMPLICIT 97 | 98 | def parse(self, code: str) -> Union[CodeElement, Expression]: 99 | try: 100 | first_parse = parse( 101 | filename=INPUT_FILENAME, 102 | code=code, 103 | code_type="expr", 104 | expected_type=Expression, 105 | ) 106 | if isinstance(first_parse, ExprFuncCall): 107 | return parse( 108 | filename=INPUT_FILENAME, 109 | code=code, 110 | code_type="code_element", 111 | expected_type=CodeElement, 112 | ) 113 | else: 114 | return first_parse 115 | except ParserError: 116 | print("reached parse error") 117 | return parse( 118 | filename=INPUT_FILENAME, 119 | code=code, 120 | code_type="code_element", 121 | expected_type=CodeElement, 122 | ) 123 | finally: 124 | print("reached here") 125 | 126 | def preprocess_parser(self, code: str): 127 | 128 | code = code.split('\n') 129 | # Comments will be ignored 130 | code = [line.split('#')[0] for line in code] 131 | 132 | # Supports extra new lines 133 | code = [line for line in code if line != ''] 134 | return '\n'.join(code) 135 | 136 | def is_func_def(self, code:str): 137 | return code.strip().startswith('func ') 138 | 139 | # List of instructions that generate and end statement 140 | triggers_end = ['if'] 141 | 142 | def func_def_ends_index(self, list_instructions): 143 | count = 0 144 | i = 0 145 | while i <= len(list_instructions): 146 | if list_instructions[i].strip().startswith('end'): 147 | if count == 0: 148 | return i 149 | else: 150 | count -= 1 151 | if list_instructions[i].strip().startswith(tuple(self.triggers_end)): 152 | count += 1 153 | i += 1 154 | 155 | def is_struct_def(self, code:str): 156 | return code.strip().startswith('struct ') 157 | 158 | def struct_def_ends_index(self, list_instructions): 159 | i = 0 160 | while i <= len(list_instructions): 161 | if list_instructions[i].strip().startswith('end'): 162 | return i 163 | i += 1 164 | 165 | def is_multiple_imports(self, code:str): 166 | return code.strip().endswith('import (') 167 | 168 | def multiple_imports_end_index(self, list_instructions): 169 | i = 0 170 | while i <= len(list_instructions): 171 | if ')' in list_instructions[i]: 172 | return i 173 | else: 174 | i += 1 175 | 176 | def get_instructions(self, code:str): 177 | list_instructions = code.split('\n') 178 | output_instructions = [] 179 | i = 0 180 | while i < len(list_instructions): 181 | current = list_instructions[i] 182 | 183 | if not self.is_func_def(current) \ 184 | and not self.is_multiple_imports(current)\ 185 | and not self.is_struct_def(current): 186 | output_instructions.append(current) 187 | i += 1 188 | else: 189 | if self.is_func_def(current): 190 | end_index = self.func_def_ends_index(list_instructions[i:]) 191 | elif self.is_struct_def(current): 192 | end_index = self.struct_def_ends_index(list_instructions[i:]) 193 | else: 194 | end_index = self.multiple_imports_end_index(list_instructions[i:]) 195 | list_instructions = [instruction.strip() for instruction in list_instructions] 196 | output_instructions.append('\n'.join(list_instructions[i: i + end_index + 1])) 197 | i += end_index + 1 198 | 199 | return output_instructions 200 | 201 | def run(self, code: str): 202 | code = self.preprocess_parser(code) 203 | list_instructions = self.get_instructions(code) 204 | for instruction in list_instructions: 205 | obj = self.parse(instruction) 206 | 207 | if isinstance(obj, CodeElement): 208 | self.exec(obj) 209 | elif isinstance(obj, Expression): 210 | value = self.eval(obj) 211 | return value 212 | else: 213 | raise NotImplementedError(f"Unsupported type: {type(obj).__name__}") 214 | 215 | def exec(self, code_element: CodeElement): 216 | # If the code element is an import statement or a function, we don't need to run 217 | # the corresponding generated instructions. 218 | skip_execution = isinstance(code_element, (CodeElementImport, CodeElementFunction)) 219 | 220 | # Replace code_element with a scoped version. 221 | code_element = CodeElementScoped(scope=MAIN_SCOPE, code_elements=[code_element]) 222 | 223 | # Find used modules (due to imports). 224 | direct_dependencies_collector = DirectDependenciesCollector() 225 | direct_dependencies_collector.visit(code_element) 226 | 227 | import_collector = ImportsCollector(self.module_reader.read) 228 | for pkg_name, location in direct_dependencies_collector.packages: 229 | import_collector.collect(pkg_name, location=location) 230 | 231 | # Create a list of code elements. 232 | elements: List[AstNode] = [] 233 | for module_name, ast in import_collector.collected_data.items(): 234 | scope = ScopedName.from_string(module_name) 235 | elements.append(CairoModule(cairo_file=ast, module_name=scope)) 236 | elements.append(code_element) 237 | 238 | elements = list(map(self.unique_label_creator.visit, elements)) 239 | 240 | # Collect identifiers and update the preprocessor with the new identifiers. 241 | for element in elements: 242 | self.identifier_collector.visit(element) 243 | 244 | new_identifiers = self.identifier_collector.identifiers 245 | 246 | # Filter out identifiers that were already defined. 247 | new_identifiers = new_identifiers.exclude(self.identifiers) 248 | self.preprocessor.update_identifiers(new_identifiers) 249 | 250 | # Store the flow_tracking data before calling the preprocessor. 251 | old_flow_tracking = self.preprocessor.flow_tracking.data 252 | 253 | for element in elements: 254 | self.struct_collector.visit(element) 255 | 256 | # Invoke the preprocessor. 257 | for element in elements: 258 | self.preprocessor.visit(element) 259 | 260 | self.preprocessor.resolve_labels() 261 | 262 | preprocessed_program = self.preprocessor.get_program() 263 | # TODO(lior, 01/10/2021): Consider assembling only the new instructions that were added. 264 | program = assemble( 265 | preprocessed_program=preprocessed_program, main_scope=MAIN_SCOPE, add_debug_info=True 266 | ) 267 | for i, value in enumerate(program.data): 268 | self.runner.memory[self.runner.program_base + i] = value 269 | self.runner.vm.load_hints(program=program, program_base=self.runner.program_base) 270 | 271 | new_pc = len(program.data) 272 | try: 273 | if skip_execution: 274 | self.preprocessor.flow_tracking.data = old_flow_tracking 275 | else: 276 | self.runner.run_until_pc(self.runner.program_base + new_pc) 277 | finally: 278 | # Move pc to the end, even if there has been a runtime error. 279 | self.runner.vm.run_context.pc = self.runner.program_base + new_pc 280 | 281 | def eval(self, expr: Expression): 282 | with self.preprocessor.scoped(new_scope=MAIN_SCOPE, parent=None): 283 | expr = self.preprocessor.simplify_expr_as_felt(expr) 284 | return ExpressionEvaluator( 285 | prime=PRIME, 286 | ap=self.runner.vm.run_context.ap, 287 | fp=self.runner.vm.run_context.fp, 288 | # TODO(lior, 01/10/2021): Replace memory with a proxy that handles auto-deduction. 289 | memory=self.runner.vm.run_context.memory, 290 | identifiers=self.identifiers, 291 | ).eval(expr) 292 | 293 | def is_repl_line_complete(self, code: str) -> bool: 294 | """ 295 | Returns True if the given code is complete and does not require additional code lines. 296 | For example: "1 + 2" is complete, while "func foo():" is not. 297 | """ 298 | if len(code.strip()) == 0: 299 | return True 300 | try: 301 | self.parse(code=code) 302 | except LocationError as exc: 303 | if "Unexpected end-of-input" in str(exc): 304 | return False 305 | return True 306 | --------------------------------------------------------------------------------