mirror of
https://github.com/ProvableHQ/leo.git
synced 2024-10-07 11:37:56 +03:00
Merge branch 'master' into leo-rfc
This commit is contained in:
commit
0952324d09
@ -1,5 +1,2 @@
|
||||
[build]
|
||||
[target.'cfg(any(not(target_arch = "wasm32"), feature = "noconfig"))']
|
||||
rustflags = ["-C", "target-cpu=native"]
|
||||
|
||||
[net]
|
||||
git-fetch-with-cli = true
|
||||
|
246
.circleci/config.yml
Normal file
246
.circleci/config.yml
Normal file
@ -0,0 +1,246 @@
|
||||
version: 2.1
|
||||
commands:
|
||||
setup_environment:
|
||||
description: "Setup environment"
|
||||
parameters:
|
||||
cache_key:
|
||||
type: string
|
||||
default: leo-stable-cache
|
||||
steps:
|
||||
- run: set -e
|
||||
- setup_remote_docker
|
||||
- run:
|
||||
name: Prepare environment and install dependencies
|
||||
command: |
|
||||
export SCCACHE_CACHE_SIZE=200M
|
||||
export WORK_DIR="$CIRCLE_WORKING_DIRECTORY/.cache/sccache"
|
||||
export SCCACHE_DIR="$CIRCLE_WORKING_DIRECTORY/.cache/sccache"
|
||||
mkdir -p "$CIRCLE_WORKING_DIRECTORY/.bin"
|
||||
wget https://github.com/mozilla/sccache/releases/download/0.2.13/sccache-0.2.13-x86_64-unknown-linux-musl.tar.gz
|
||||
tar -C "$CIRCLE_WORKING_DIRECTORY/.bin" -xvf sccache-0.2.13-x86_64-unknown-linux-musl.tar.gz
|
||||
mv $CIRCLE_WORKING_DIRECTORY/.bin/sccache-0.2.13-x86_64-unknown-linux-musl/sccache $CIRCLE_WORKING_DIRECTORY/.bin/sccache
|
||||
export PATH="$PATH:$CIRCLE_WORKING_DIRECTORY/.bin"
|
||||
export RUSTC_WRAPPER="sccache"
|
||||
rm -rf "$CIRCLE_WORKING_DIRECTORY/.cargo/registry"
|
||||
sudo apt-get update && sudo apt-get install -y clang llvm-dev llvm pkg-config xz-utils make libssl-dev libssl-dev
|
||||
- restore_cache:
|
||||
keys:
|
||||
- << parameters.cache_key >>
|
||||
clear_environment:
|
||||
description: "Clear environment"
|
||||
parameters:
|
||||
cache_key:
|
||||
type: string
|
||||
default: leo-stable-cache
|
||||
steps:
|
||||
- run: (sccache -s||true)
|
||||
- run: set +e
|
||||
- save_cache:
|
||||
key: << parameters.cache_key >>
|
||||
paths:
|
||||
- .cache/sccache
|
||||
- .cargo
|
||||
jobs:
|
||||
|
||||
rust-stable:
|
||||
docker:
|
||||
- image: cimg/rust:1.51.0
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- checkout
|
||||
- setup_environment:
|
||||
cache_key: leo-stable-cache
|
||||
- run:
|
||||
name: Build and run tests
|
||||
no_output_timeout: 30m
|
||||
command: cargo test --all
|
||||
- persist_to_workspace:
|
||||
root: ~/
|
||||
paths: project/
|
||||
- clear_environment:
|
||||
cache_key: leo-stable-cache
|
||||
|
||||
rust-nightly:
|
||||
docker:
|
||||
- image: howardwu/snarkos-ci:2021-03-25
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- checkout
|
||||
- setup_environment:
|
||||
cache_key: leo-nightly-cache
|
||||
- run:
|
||||
name: Build and test
|
||||
no_output_timeout: 30m
|
||||
command: cargo test --all
|
||||
- clear_environment:
|
||||
cache_key: leo-nightly-cache
|
||||
|
||||
leo-executable:
|
||||
docker:
|
||||
- image: cimg/rust:1.51.0
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- checkout
|
||||
- setup_environment:
|
||||
cache_key: leo-executable-cache
|
||||
- run:
|
||||
name: Build and install Leo
|
||||
no_output_timeout: 30m
|
||||
command: cargo install --path . --root . --locked
|
||||
- persist_to_workspace:
|
||||
root: ~/
|
||||
paths: project/
|
||||
- clear_environment:
|
||||
cache_key: leo-executable-cache
|
||||
|
||||
leo-new:
|
||||
docker:
|
||||
- image: cimg/rust:1.51.0
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /home/circleci/project/
|
||||
- run:
|
||||
name: leo new
|
||||
command: |
|
||||
export LEO=/home/circleci/project/project/bin/leo
|
||||
./project/.circleci/leo-new.sh
|
||||
|
||||
leo-init:
|
||||
docker:
|
||||
- image: cimg/rust:1.51.0
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /home/circleci/project/
|
||||
- run:
|
||||
name: leo init
|
||||
command: |
|
||||
export LEO=/home/circleci/project/project/bin/leo
|
||||
./project/.circleci/leo-init.sh
|
||||
|
||||
leo-clean:
|
||||
docker:
|
||||
- image: cimg/rust:1.51.0
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /home/circleci/project/
|
||||
- run:
|
||||
name: leo clean
|
||||
command: |
|
||||
export LEO=/home/circleci/project/project/bin/leo
|
||||
./project/.circleci/leo-clean.sh
|
||||
|
||||
leo-setup:
|
||||
docker:
|
||||
- image: cimg/rust:1.51.0
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /home/circleci/project/
|
||||
- run:
|
||||
name: leo setup
|
||||
command: |
|
||||
export LEO=/home/circleci/project/project/bin/leo
|
||||
./project/.circleci/leo-setup.sh
|
||||
|
||||
leo-add-remove:
|
||||
docker:
|
||||
- image: cimg/rust:1.51.0
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /home/circleci/project/
|
||||
- run:
|
||||
name: leo add & remove
|
||||
command: |
|
||||
export LEO=/home/circleci/project/project/bin/leo
|
||||
./project/.circleci/leo-add-remove.sh
|
||||
|
||||
leo-check-constraints:
|
||||
docker:
|
||||
- image: cimg/rust:1.50.0
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /home/circleci/project/
|
||||
- run:
|
||||
name: leo check constraints for Pedersen Hash
|
||||
command: |
|
||||
export LEO=/home/circleci/project/project/bin/leo
|
||||
./project/.circleci/leo-check-constraints.sh
|
||||
|
||||
leo-login-logout:
|
||||
docker:
|
||||
- image: cimg/rust:1.51.0
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /home/circleci/project/
|
||||
- run:
|
||||
name: leo login & logout
|
||||
command: |
|
||||
export LEO=/home/circleci/project/project/bin/leo
|
||||
./project/.circleci/leo-login-logout.sh
|
||||
|
||||
leo-clone:
|
||||
docker:
|
||||
- image: cimg/rust:1.51.0
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /home/circleci/project/
|
||||
- run:
|
||||
name: leo clone
|
||||
command: |
|
||||
export LEO=/home/circleci/project/project/bin/leo
|
||||
./project/.circleci/leo-clone.sh
|
||||
|
||||
leo-publish:
|
||||
docker:
|
||||
- image: cimg/rust:1.51.0
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /home/circleci/project/
|
||||
- run:
|
||||
name: leo publish
|
||||
command: |
|
||||
export LEO=/home/circleci/project/project/bin/leo
|
||||
./project/.circleci/leo-publish.sh
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
main-workflow:
|
||||
jobs:
|
||||
- rust-stable
|
||||
- rust-nightly
|
||||
- leo-executable
|
||||
- leo-new:
|
||||
requires:
|
||||
- leo-executable
|
||||
- leo-init:
|
||||
requires:
|
||||
- leo-executable
|
||||
- leo-clean:
|
||||
requires:
|
||||
- leo-executable
|
||||
- leo-setup:
|
||||
requires:
|
||||
- leo-executable
|
||||
- leo-add-remove:
|
||||
requires:
|
||||
- leo-executable
|
||||
- leo-check-constraints:
|
||||
requires:
|
||||
- leo-executable
|
||||
- leo-login-logout:
|
||||
requires:
|
||||
- leo-executable
|
||||
- leo-clone:
|
||||
requires:
|
||||
- leo-executable
|
||||
- leo-publish:
|
||||
requires:
|
||||
- leo-executable
|
6
.circleci/leo-add-remove.sh
Executable file
6
.circleci/leo-add-remove.sh
Executable file
@ -0,0 +1,6 @@
|
||||
# leo add (w/o login) & remove
|
||||
|
||||
$LEO new my-app && cd my-app || exit 1
|
||||
$LEO add howard/silly-sudoku
|
||||
$LEO remove silly-sudoku
|
||||
$LEO clean
|
16
.circleci/leo-check-constraints.sh
Executable file
16
.circleci/leo-check-constraints.sh
Executable file
@ -0,0 +1,16 @@
|
||||
# leo new hello-world
|
||||
|
||||
cd ./project/examples/pedersen-hash
|
||||
|
||||
export PEDERSEN_HASH_CONSTRAINTS=1539;
|
||||
|
||||
# line that we're searching for is:
|
||||
# `Build Number of constraints - 1539`
|
||||
export ACTUAL_CONSTRAINTS=$($LEO build | grep constraints | awk '{print $NF}')
|
||||
|
||||
# if else expression with only else block
|
||||
[[ PEDERSEN_HASH_CONSTRAINTS -eq ACTUAL_CONSTRAINTS ]] || {
|
||||
echo >&2 "Number of constraints for Pedersen Hash is not $PEDERSEN_HASH_CONSTRAINTS";
|
||||
echo >&2 "Real number of constraints is $ACTUAL_CONSTRAINTS";
|
||||
exit 1;
|
||||
}
|
34
.circleci/leo-clean.sh
Executable file
34
.circleci/leo-clean.sh
Executable file
@ -0,0 +1,34 @@
|
||||
# leo new hello-world
|
||||
|
||||
$LEO new hello-world
|
||||
ls -la
|
||||
cd hello-world && ls -la
|
||||
$LEO run
|
||||
|
||||
# Assert that the 'outputs' folder is not empty
|
||||
|
||||
cd outputs || exit 1
|
||||
if [ "$(ls -A $DIR)" ]; then
|
||||
echo "$DIR is not empty"
|
||||
else
|
||||
echo "$DIR is empty"
|
||||
exit 1
|
||||
fi
|
||||
cd ..
|
||||
|
||||
# leo clean
|
||||
|
||||
$LEO clean
|
||||
cd outputs && ls -la
|
||||
cd ..
|
||||
|
||||
# Assert that the 'outputs' folder is empty
|
||||
|
||||
cd outputs || exit 1
|
||||
if [ "$(ls -A $DIR)" ]; then
|
||||
echo "$DIR is not empty"
|
||||
exit 1
|
||||
else
|
||||
echo "$DIR is empty"
|
||||
exit 0
|
||||
fi
|
18
.circleci/leo-clone.sh
Executable file
18
.circleci/leo-clone.sh
Executable file
@ -0,0 +1,18 @@
|
||||
# leo clone
|
||||
|
||||
# Clone the test-app package.
|
||||
export PACKAGE="$ALEO_PM_USERNAME/test-app"
|
||||
$LEO clone $PACKAGE
|
||||
|
||||
# Assert that the 'test-app' folder is not empty
|
||||
|
||||
cd test-app || exit 1
|
||||
if [ "$(ls -A $DIR)" ]; then
|
||||
echo "$DIR is not empty"
|
||||
else
|
||||
echo "$DIR is empty"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ls -la
|
||||
$LEO run
|
5
.circleci/leo-init.sh
Executable file
5
.circleci/leo-init.sh
Executable file
@ -0,0 +1,5 @@
|
||||
mkdir hello-world
|
||||
cd hello-world
|
||||
$LEO init || exit 1
|
||||
ls -la hello-world
|
||||
$LEO run
|
15
.circleci/leo-login-logout.sh
Executable file
15
.circleci/leo-login-logout.sh
Executable file
@ -0,0 +1,15 @@
|
||||
# leo login & logout
|
||||
|
||||
$LEO login -u "$ALEO_PM_USERNAME" -p "$ALEO_PM_PASSWORD"
|
||||
$LEO new my-app && cd my-app || exit 1
|
||||
|
||||
cat Leo.toml
|
||||
|
||||
# verify that in Leo.toml there's no line with [AUTHOR];
|
||||
# since CI does not allow showing credentials, we won't see it in the file;
|
||||
# so the only way to test is to make sure that there's just no [AUTHOR] there
|
||||
[[ $(cat Leo.toml | grep "\[AUTHOR\]" | wc -l) -eq 0 ]] || exit 1
|
||||
|
||||
$LEO add howard/silly-sudoku
|
||||
$LEO remove silly-sudoku
|
||||
$LEO logout
|
9
.circleci/leo-new.sh
Executable file
9
.circleci/leo-new.sh
Executable file
@ -0,0 +1,9 @@
|
||||
$LEO new hello-world
|
||||
ls -la
|
||||
cd hello-world && ls -la
|
||||
|
||||
# verify that in Leo.toml there's a placeholder for author
|
||||
# because at the time of calling `leo new` user is not logged in
|
||||
[[ $(cat Leo.toml | grep "\[AUTHOR\]" | wc -l) -eq 1 ]] || exit 1
|
||||
|
||||
$LEO run
|
56
.circleci/leo-publish.sh
Executable file
56
.circleci/leo-publish.sh
Executable file
@ -0,0 +1,56 @@
|
||||
# leo login, publish and logout
|
||||
|
||||
# Login
|
||||
$LEO login -u "$ALEO_PM_USERNAME" -p "$ALEO_PM_PASSWORD"
|
||||
|
||||
# Clone the test-app package.
|
||||
export PACKAGE="$ALEO_PM_USERNAME/test-app"
|
||||
$LEO clone $PACKAGE
|
||||
cd test-app || exit 1
|
||||
|
||||
# Fetch the current Leo package version number.
|
||||
#
|
||||
# 1. Print out the Leo.toml file.
|
||||
# 2. Search for a line with the word "version".
|
||||
# 3. Isolate that into a single line.
|
||||
# 4. Split the line from the '=' sign and keep the right-hand side.
|
||||
# 5. Remove the quotes around the version number.
|
||||
# 6. Trim any excess whitespace.
|
||||
export CURRENT=$(cat Leo.toml \
|
||||
| grep version \
|
||||
| head -1 \
|
||||
| awk -F= '{ print $2 }' \
|
||||
| sed 's/[",]//g' \
|
||||
| xargs)
|
||||
|
||||
# Increment the current Leo package version number by 1.
|
||||
#
|
||||
# 1. Print out the Leo.toml file.
|
||||
# 2. Search for a line with the word "version".
|
||||
# 3. Isolate that into a single line.
|
||||
# 4. Split the line from the '=' sign and keep the right-hand side.
|
||||
# 5. Remove the quotes around the version number.
|
||||
# 6. Trim any excess whitespace.
|
||||
# 7. Increment the version number by 1 (on the semver patch).
|
||||
#
|
||||
# https://stackoverflow.com/questions/8653126/how-to-increment-version-number-in-a-shell-script
|
||||
export UPDATED=$(cat Leo.toml \
|
||||
| grep version \
|
||||
| head -1 \
|
||||
| awk -F= '{ print $2 }' \
|
||||
| sed 's/[",]//g' \
|
||||
| xargs \
|
||||
| awk -F. -v OFS=. 'NF==1{print ++$NF}; NF>1{if(length($NF+1)>length($NF))$(NF-1)++; $NF=sprintf("%0*d", length($NF), ($NF+1)%(10^length($NF))); print}')
|
||||
|
||||
# Write the updated Leo package version number to the Leo.toml file.
|
||||
export TOML=$(cat Leo.toml | sed "s/$CURRENT/$UPDATED/g")
|
||||
echo "$TOML" > Leo.toml
|
||||
|
||||
# Run the package to confirm the manifest remains well-formed.
|
||||
$LEO run
|
||||
|
||||
# Publish the package to Aleo.pm
|
||||
$LEO publish
|
||||
|
||||
# Logout
|
||||
$LEO logout
|
7
.circleci/leo-setup.sh
Executable file
7
.circleci/leo-setup.sh
Executable file
@ -0,0 +1,7 @@
|
||||
# leo setup
|
||||
|
||||
cd ./project/examples/pedersen-hash || exit 1
|
||||
$LEO setup
|
||||
$LEO setup
|
||||
$LEO setup --skip-key-check
|
||||
$LEO clean
|
73
.github/workflows/ci-binary.yml
vendored
73
.github/workflows/ci-binary.yml
vendored
@ -1,73 +0,0 @@
|
||||
name: Leo Binary Tests
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'documentation/**'
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
|
||||
jobs:
|
||||
new:
|
||||
name: Hello Leo ('leo new hello-world')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly
|
||||
override: true
|
||||
components: rustfmt
|
||||
|
||||
- name: Install Leo
|
||||
uses: actions-rs/cargo@v1
|
||||
env:
|
||||
CARGO_NET_GIT_FETCH_WITH_CLI: true
|
||||
with:
|
||||
command: install
|
||||
args: --path .
|
||||
|
||||
- name: 'leo new hello-world'
|
||||
run: |
|
||||
cd ..
|
||||
leo new hello-world
|
||||
ls -la
|
||||
cd hello-world && ls -la
|
||||
leo run
|
||||
|
||||
init:
|
||||
name: Hello Leo ('leo init')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: nightly
|
||||
override: true
|
||||
components: rustfmt
|
||||
|
||||
- name: Install Leo
|
||||
uses: actions-rs/cargo@v1
|
||||
env:
|
||||
CARGO_NET_GIT_FETCH_WITH_CLI: true
|
||||
with:
|
||||
command: install
|
||||
args: --path .
|
||||
|
||||
- name: 'leo init'
|
||||
run: |
|
||||
cd .. && mkdir hello-world && cd hello-world
|
||||
leo init
|
||||
ls -la
|
||||
leo run
|
63
.github/workflows/ci.yml
vendored
63
.github/workflows/ci.yml
vendored
@ -4,6 +4,8 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- staging
|
||||
- trying
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- 'documentation/**'
|
||||
@ -39,21 +41,15 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- stable
|
||||
- nightly
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Rust (${{ matrix.rust }})
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust }}
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: clippy
|
||||
|
||||
@ -68,63 +64,12 @@ jobs:
|
||||
with:
|
||||
command: clippy
|
||||
args: --examples --all-features --all
|
||||
if: matrix.rust == 'stable'
|
||||
|
||||
- name: Check benchmarks on nightly
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: clippy
|
||||
args: --all-features --examples --all --benches
|
||||
if: matrix.rust == 'nightly'
|
||||
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
# env:
|
||||
# RUSTFLAGS: -Dwarnings
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- stable
|
||||
- nightly
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Rust (${{ matrix.rust }})
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: ${{ matrix.rust }}
|
||||
override: true
|
||||
|
||||
# - name: Check examples
|
||||
# uses: actions-rs/cargo@v1
|
||||
# env:
|
||||
# CARGO_NET_GIT_FETCH_WITH_CLI: true
|
||||
# with:
|
||||
# command: check
|
||||
# args: --examples --all
|
||||
#
|
||||
# - name: Check examples with all features on stable
|
||||
# uses: actions-rs/cargo@v1
|
||||
# with:
|
||||
# command: check
|
||||
# args: --examples --all-features --all
|
||||
# if: matrix.rust == 'stable'
|
||||
#
|
||||
# - name: Check benchmarks on nightly
|
||||
# uses: actions-rs/cargo@v1
|
||||
# with:
|
||||
# command: check
|
||||
# args: --all-features --examples --all --benches
|
||||
# if: matrix.rust == 'nightly'
|
||||
|
||||
- name: Test
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --all --features ci_skip --no-fail-fast
|
||||
|
||||
test-package:
|
||||
name: Test Package
|
||||
|
23
.github/workflows/release.yml
vendored
23
.github/workflows/release.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
||||
|
||||
- name: Build Leo
|
||||
run: |
|
||||
cargo build --all --release && strip target/release/leo
|
||||
cargo build --all --release --features noconfig && strip target/release/leo
|
||||
env:
|
||||
CARGO_NET_GIT_FETCH_WITH_CLI: true
|
||||
|
||||
@ -67,7 +67,7 @@ jobs:
|
||||
|
||||
- name: Build Leo
|
||||
run: |
|
||||
cargo build --all --release && strip target/release/leo
|
||||
cargo build --all --release --features noconfig && strip target/release/leo
|
||||
env:
|
||||
CARGO_NET_GIT_FETCH_WITH_CLI: true
|
||||
|
||||
@ -95,7 +95,6 @@ jobs:
|
||||
windows:
|
||||
name: Windows
|
||||
runs-on: windows-latest
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
@ -111,12 +110,15 @@ jobs:
|
||||
- name: Install LLVM and Clang
|
||||
uses: KyleMayes/install-llvm-action@v1
|
||||
with:
|
||||
version: "10.0"
|
||||
directory: ~ / .clang
|
||||
version: "11"
|
||||
directory: ${{ runner.temp }}/llvm
|
||||
|
||||
- name: Set LIBCLANG_PATH
|
||||
run: echo "LIBCLANG_PATH=$((gcm clang).source -replace "clang.exe")" >> $env:GITHUB_ENV
|
||||
|
||||
- name: Build Leo
|
||||
run: |
|
||||
cargo build --all --release
|
||||
cargo build --all --release --features noconfig
|
||||
env:
|
||||
CARGO_NET_GIT_FETCH_WITH_CLI: true
|
||||
|
||||
@ -125,18 +127,13 @@ jobs:
|
||||
|
||||
- name: Zip
|
||||
run: |
|
||||
mkdir tempdir
|
||||
mv target/release/leo tempdir
|
||||
cd tempdir
|
||||
Compress-Archive leo-${{ steps.get_version.outputs.version }}-x86_64-pc-windows-gnu leo
|
||||
cd ..
|
||||
mv leo-${{ steps.get_version.outputs.version }}-x86_64-pc-windows-gnu .
|
||||
Compress-Archive target/release/leo.exe leo-${{ steps.get_version.outputs.version }}-x86_64-pc-windows-msvc.zip
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
with:
|
||||
files: |
|
||||
leo-${{ steps.get_version.outputs.version }}-x86_64-pc-windows-gnu.zip
|
||||
leo-${{ steps.get_version.outputs.version }}-x86_64-pc-windows-msvc.zip
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -2,3 +2,9 @@
|
||||
/tmp/
|
||||
**.idea/
|
||||
*.DS_Store
|
||||
|
||||
**/process.yml
|
||||
|
||||
**/.crates.toml
|
||||
**/.crates2.json
|
||||
**/bin/
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"**/*.rs": [
|
||||
"// Copyright (C) 2019-2020 Aleo Systems Inc.",
|
||||
"// Copyright (C) 2019-2021 Aleo Systems Inc.",
|
||||
"// This file is part of the Leo library.",
|
||||
"",
|
||||
"// The Leo library is free software: you can redistribute it and/or modify",
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 29 KiB |
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2020 Aleo Systems Inc.
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
|
1
.resources/release-version
Normal file
1
.resources/release-version
Normal file
@ -0,0 +1 @@
|
||||
v1.4.0
|
@ -10,7 +10,7 @@ use_try_shorthand = true
|
||||
# Nightly configurations
|
||||
imports_layout = "HorizontalVertical"
|
||||
license_template_path = ".resources/license_header"
|
||||
merge_imports = true
|
||||
imports_granularity = "Crate"
|
||||
overflow_delimited_expr = true
|
||||
reorder_impl_items = true
|
||||
version = "Two"
|
||||
|
@ -1,5 +1,5 @@
|
||||
[hooks]
|
||||
pre-commit = "cargo +nightly clippy && cargo +nightly fmt --all -- --check"
|
||||
pre-commit = "cargo clippy && cargo +nightly fmt --all -- --check"
|
||||
|
||||
[logging]
|
||||
verbose = true
|
||||
|
70
.travis.yml
70
.travis.yml
@ -1,70 +0,0 @@
|
||||
language: rust
|
||||
|
||||
before_install:
|
||||
- set -e
|
||||
- export SCCACHE_CACHE_SIZE=200M
|
||||
- export SCCACHE_DIR="$TRAVIS_HOME/.cache/sccache"
|
||||
- mkdir "$TRAVIS_HOME/.bin"
|
||||
- wget https://github.com/mozilla/sccache/releases/download/0.2.13/sccache-0.2.13-x86_64-unknown-linux-musl.tar.gz
|
||||
- tar -C "$TRAVIS_HOME/.bin" -xvf sccache-0.2.13-x86_64-unknown-linux-musl.tar.gz
|
||||
- mv $TRAVIS_HOME/.bin/sccache-0.2.13-x86_64-unknown-linux-musl/sccache $TRAVIS_HOME/.bin/sccache
|
||||
- export PATH="$PATH:$TRAVIS_HOME/.bin"
|
||||
- export RUSTC_WRAPPER="sccache"
|
||||
- |
|
||||
declare -r SSH_FILE="$(mktemp -u $HOME/.ssh/XXXXX)"
|
||||
openssl aes-256-cbc -K $encrypted_beefc4a47cdc_key -iv $encrypted_beefc4a47cdc_iv -in .travis/travis-snarkvm.enc -out $SSH_FILE -d
|
||||
chmod 600 "$SSH_FILE" \
|
||||
&& printf "%s\n" \
|
||||
"Host github.com" \
|
||||
" IdentityFile $SSH_FILE" \
|
||||
" LogLevel ERROR" >> ~/.ssh/config
|
||||
- git clone --progress --verbose git@github.com:AleoHQ/snarkOS.git
|
||||
- mv snarkOS ..
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $TRAVIS_HOME/.cache/sccache
|
||||
- $TRAVIS_HOME/.cargo
|
||||
|
||||
# See https://levans.fr/rust_travis_cache.html
|
||||
before_cache:
|
||||
- rm -rf "$TRAVIS_HOME/.cargo/registry"
|
||||
|
||||
after_script:
|
||||
- (sccache -s||true)
|
||||
- set +e
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- rust: stable
|
||||
env: TEST_COVERAGE=1
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libcurl4-openssl-dev
|
||||
- libelf-dev
|
||||
- libdw-dev
|
||||
- cmake
|
||||
- gcc
|
||||
- binutils-dev
|
||||
- libiberty-dev
|
||||
script:
|
||||
- cargo test --all
|
||||
after_success:
|
||||
- wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz
|
||||
- tar xzf master.tar.gz && cd kcov-master
|
||||
- mkdir build && cd build && cmake .. && make && sudo make install
|
||||
- cd ../.. && rm -rf kcov-master
|
||||
- for file in target/debug/deps/*-*; do if [[ "$file" != *\.* ]]; then mkdir -p "target/cov/$(basename $file)"; kcov --exclude-pattern=/.cargo,/usr/lib --verify "target/cov/$(basename $file)" "$file"; fi done
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
- echo "Uploaded code coverage"
|
||||
- rust: nightly-2020-03-18
|
||||
install:
|
||||
- rustup component add rustfmt
|
||||
script:
|
||||
- cargo fmt -- --check
|
||||
- cargo test --all
|
||||
|
||||
script:
|
||||
- echo "leo"
|
Binary file not shown.
88
CONTRIBUTING.md
Normal file
88
CONTRIBUTING.md
Normal file
@ -0,0 +1,88 @@
|
||||
# Contributing
|
||||
|
||||
Thank you for your interest in contributing to Leo! Below you can find some guidelines that the project strives to follow.
|
||||
|
||||
## Pull requests
|
||||
|
||||
Please follow the instructions below when filing pull requests:
|
||||
|
||||
- Ensure that your branch is forked from the current [master](https://github.com/AleoHQ/leo/tree/master) branch.
|
||||
- Fill out the provided markdown template for the feature or proposal. Be sure to link the pull request to any issues by using keywords. Example: "closes #130".
|
||||
- Run `cargo fmt` before you commit; we use the `nightly` version of `rustfmt` to format the code, so you'll need to have the `nightly` toolchain installed on your machine; there's a [git hook](https://git-scm.com/docs/githooks) that ensures proper formatting before any commits can be made, and [`.rustfmt.toml`](https://github.com/AleoHQ/Leo/blob/master/.rustfmt.toml) specifies some of the formatting conventions.
|
||||
- Run `cargo clippy` to ensure that popular correctness and performance pitfalls are avoided.
|
||||
|
||||
## Style
|
||||
|
||||
These guidelines ensure consistent readable rust code within the Leo repository.
|
||||
|
||||
### Comments
|
||||
|
||||
Prefer line comments (//) to block comments (/* ... */).
|
||||
|
||||
When using single-line block comments there should be a single space after the opening sigil and before the closing sigil. Multi-line block comments should have a newline after the opening sigil and before the closing sigil.
|
||||
|
||||
Prefer to put a comment on its own line. Where a comment follows code, there should be a single space before it. Where a block comment is inline, there should be surrounding whitespace as if it were an identifier or keyword. There should be no trailing whitespace after a comment or at the end of any line in a multi-line comment. Examples:
|
||||
|
||||
```rust
|
||||
// A comment on an item.
|
||||
struct Foo { ... }
|
||||
|
||||
fn foo() {} // A comment after an item.
|
||||
|
||||
pub fn foo(/* a comment before an argument */ x: T) {...}
|
||||
```
|
||||
|
||||
Comments should be complete sentences. Start with a capital letter, end with a period (.). An inline block comment may be treated as a note without punctuation.
|
||||
|
||||
### Imports
|
||||
|
||||
* Stated at the top of the file
|
||||
* Ordered alphabetically
|
||||
* Split in two sections
|
||||
* First party: crate imports + Aleo imports (example: snarkVM)
|
||||
* Third party: rust std + everything else
|
||||
|
||||
Example:
|
||||
```rust
|
||||
|
||||
use crate::Circuit;
|
||||
use leo_ast::IntegerType;
|
||||
|
||||
use serde::Serialize;
|
||||
use std::{
|
||||
fmt,
|
||||
sync::{Arc, Weak},
|
||||
};
|
||||
```
|
||||
|
||||
`rust fmt` should automatically sort imports alphabetically after they are split into the appropriate sections.
|
||||
|
||||
## Coding conventions
|
||||
|
||||
Leo is a big project, so (non-)adherence to best practices related to performance can have a considerable impact; below are the rules we try to follow at all times in order to ensure high quality of the code:
|
||||
|
||||
### Memory handling
|
||||
- If the final size is known, pre-allocate the collections (`Vec`, `HashMap` etc.) using `with_capacity` or `reserve` - this ensures that there are both fewer allocations (which involve system calls) and that the final allocated capacity is as close to the required size as possible.
|
||||
- Create the collections right before they are populated/used, as opposed to e.g. creating a few big ones at the beginning of a function and only using them later on; this reduces the amount of time they occupy memory.
|
||||
- If an intermediate vector is avoidable, use an `Iterator` instead; most of the time this just amounts to omitting the call to `.collect()` if a single-pass iteraton follows afterwards, or returning an `impl Iterator<Item = T>` from a function when the caller only needs to iterate over that result once.
|
||||
- When possible, fill/resize collections "in bulk" instead of pushing a single element in a loop; this is usually (but not always) detected by `clippy`, suggesting to create vectors containing a repeated value with `vec![x; N]` or extending them with `.resize(N, x)`.
|
||||
- When a value is to eventually be consumed in a chain of function calls, pass it by value instead of by reference; this has the following benefits:
|
||||
* It makes the fact that the value is needed by value clear to the caller, who can then potentially reclaim it from the object afterwards if it is "heavy", limiting allocations.
|
||||
* It often enables the value to be cloned fewer times (whenever it's no longer needed at the callsite).
|
||||
* When the value is consumed and is not needed afterwards, the memory it occupies is freed, improving memory utilization.
|
||||
- If a slice may or may _not_ be extended (which requires a promotion to a vector) and does not need to be consumed afterwards, consider using a [`Cow<'a, [T]>`](https://doc.rust-lang.org/std/borrow/enum.Cow.html) combined with `Cow::to_mut` instead to potentially avoid an extra allocation; an example in Leo could be conditional padding of bits.
|
||||
- Prefer arrays and temporary slices to vectors where possible; arrays are often a good choice if their final size is known in advance and isn't too great (as they are stack-bound), and a small temporary slice `&[x, y, z]` is preferable to a `vec![x, y, z]` if it's applicable.
|
||||
- If a reference is sufficient, don't use `.clone()`/`to_vec()`, which is often the case with methods on `struct`s that provide access to their contents; if they only need to be referenced, there's no need for the extra allocation.
|
||||
- Use `into_iter()` instead of `iter().cloned()` where possible, i.e. whenever the values being iterated over can be consumed altogether.
|
||||
- If possible, reuse collections; an example would be a loop that needs a clean vector on each iteration: instead of creating and allocating it over and over, create it _before_ the loop and use `.clear()` on every iteration instead.
|
||||
- Try to keep the sizes of `enum` variants uniform; use `Box<T>` on ones that are large.
|
||||
|
||||
### Misc. performance
|
||||
|
||||
- Avoid the `format!()` macro; if it is used only to convert a single value to a `String`, use `.to_string()` instead, which is also available to all the implementors of `Display`.
|
||||
- Don't check if an element belongs to a map (using `contains` or `get`) if you want to conditionally insert it too, as the return value of `insert` already indicates whether the value was present or not; use that or the `Entry` API instead.
|
||||
- If a reference is sufficient as a function parameter, use:
|
||||
* `&[T]` instead of `&Vec<T>`
|
||||
* `&str` instead of `&String`
|
||||
* `&Path` instead of `&PathBuf`
|
||||
- For `struct`s that can be compared/discerned based on some specific field(s), consider hand-written implementations of `PartialEq` **and** `Hash` ([they must match](https://doc.rust-lang.org/std/hash/trait.Hash.html#hash-and-eq)) for faster comparison and hashing.
|
1690
Cargo.lock
generated
1690
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
79
Cargo.toml
79
Cargo.toml
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "leo-lang"
|
||||
version = "1.0.8"
|
||||
version = "1.4.0"
|
||||
authors = [ "The Aleo Team <hello@aleo.org>" ]
|
||||
description = "The Leo programming language"
|
||||
homepage = "https://aleo.org"
|
||||
@ -26,70 +26,72 @@ path = "leo/main.rs"
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"asg",
|
||||
"asg-passes",
|
||||
"ast",
|
||||
"compiler",
|
||||
"core",
|
||||
"gadgets",
|
||||
"grammar",
|
||||
"imports",
|
||||
"input",
|
||||
"linter",
|
||||
"package",
|
||||
"parser",
|
||||
"state",
|
||||
"symbol-table",
|
||||
"type-inference"
|
||||
"synthesizer",
|
||||
"test-framework"
|
||||
]
|
||||
|
||||
[dependencies.leo-ast]
|
||||
path = "./ast"
|
||||
version = "1.0.8"
|
||||
version = "1.4.0"
|
||||
|
||||
[dependencies.leo-compiler]
|
||||
path = "./compiler"
|
||||
version = "1.0.8"
|
||||
|
||||
[dependencies.leo-gadgets]
|
||||
path = "./gadgets"
|
||||
version = "1.0.8"
|
||||
version = "1.4.0"
|
||||
|
||||
[dependencies.leo-imports]
|
||||
path = "./imports"
|
||||
version = "1.0.8"
|
||||
version = "1.4.0"
|
||||
|
||||
[dependencies.leo-input]
|
||||
path = "./input"
|
||||
version = "1.0.8"
|
||||
version = "1.4.0"
|
||||
|
||||
[dependencies.leo-package]
|
||||
path = "./package"
|
||||
version = "1.0.8"
|
||||
version = "1.4.0"
|
||||
|
||||
[dependencies.leo-state]
|
||||
path = "./state"
|
||||
version = "1.0.8"
|
||||
version = "1.4.0"
|
||||
|
||||
[dependencies.leo-synthesizer]
|
||||
path = "./synthesizer"
|
||||
version = "1.4.0"
|
||||
|
||||
[dependencies.snarkvm-algorithms]
|
||||
version = "0.0.2"
|
||||
default-features = false
|
||||
version = "0.2.2"
|
||||
|
||||
[dependencies.snarkvm-curves]
|
||||
version = "0.0.2"
|
||||
default-features = false
|
||||
|
||||
[dependencies.snarkvm-errors]
|
||||
version = "0.0.2"
|
||||
version = "0.2.2"
|
||||
default-features = false
|
||||
|
||||
[dependencies.snarkvm-gadgets]
|
||||
version = "0.0.2"
|
||||
version = "0.2.2"
|
||||
default-features = false
|
||||
|
||||
[dependencies.snarkvm-models]
|
||||
version = "0.0.2"
|
||||
[dependencies.snarkvm-r1cs]
|
||||
version = "0.2.2"
|
||||
default-features = false
|
||||
|
||||
[dependencies.snarkvm-utilities]
|
||||
version = "0.0.2"
|
||||
version = "0.2.2"
|
||||
|
||||
[dependencies.anyhow]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.structopt]
|
||||
version = "0.3"
|
||||
|
||||
[dependencies.clap]
|
||||
version = "2.33.3"
|
||||
@ -98,7 +100,7 @@ version = "2.33.3"
|
||||
version = "2.0"
|
||||
|
||||
[dependencies.dirs]
|
||||
version = "3.0.1"
|
||||
version = "3.0.2"
|
||||
|
||||
[dependencies.console]
|
||||
version = "0.14.0"
|
||||
@ -110,23 +112,20 @@ version = "0.3.1"
|
||||
version = "1.4.0"
|
||||
|
||||
[dependencies.notify]
|
||||
version = "4.0.15"
|
||||
|
||||
[dependencies.num-bigint]
|
||||
version = "0.3"
|
||||
version = "4.0.16"
|
||||
|
||||
[dependencies.rand]
|
||||
version = "0.7"
|
||||
version = "0.8"
|
||||
|
||||
[dependencies.rand_core]
|
||||
version = "0.6.1"
|
||||
version = "0.6.2"
|
||||
|
||||
[dependencies.reqwest]
|
||||
version = "0.11.0"
|
||||
version = "0.11.3"
|
||||
features = [ "blocking", "json", "multipart" ]
|
||||
|
||||
[dependencies.self_update]
|
||||
version = "0.22.0"
|
||||
version = "0.26.0"
|
||||
features = [ "archive-zip" ]
|
||||
|
||||
[dependencies.serde]
|
||||
@ -152,12 +151,22 @@ features = [ "fmt" ]
|
||||
[dependencies.zip]
|
||||
version = "0.5"
|
||||
|
||||
[target."cfg(windows)".dependencies.ansi_term]
|
||||
version = "0.12.1"
|
||||
|
||||
[dev-dependencies.rusty-hook]
|
||||
version = "0.11.2"
|
||||
|
||||
[dev-dependencies.assert_cmd]
|
||||
version = "1.0.3"
|
||||
|
||||
[dev-dependencies.test_dir]
|
||||
version = "0.1.0"
|
||||
|
||||
[features]
|
||||
default = [ ]
|
||||
ci_skip = [ "leo-compiler/ci_skip" ]
|
||||
noconfig = [ ]
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
|
31
DEVELOPMENT.md
Normal file
31
DEVELOPMENT.md
Normal file
@ -0,0 +1,31 @@
|
||||
# Development Guide
|
||||
|
||||
## Running CircleCI locally
|
||||
|
||||
### Step 1: Install CircleCI
|
||||
|
||||
If you wish to run CircleCI locally, start by installing it:
|
||||
|
||||
- macOS
|
||||
```
|
||||
brew install circleci
|
||||
```
|
||||
|
||||
- Linux (via Snap)
|
||||
```
|
||||
sudo snap install docker circleci
|
||||
sudo snap connect circleci:docker docker
|
||||
```
|
||||
|
||||
- Windows (via Chocolatey)
|
||||
```
|
||||
choco install circleci-cli -y
|
||||
```
|
||||
|
||||
### Step 2: Run CircleCI
|
||||
|
||||
To run a job, export the config to `process.yml`, and specify it when executing:
|
||||
```shell
|
||||
circleci config process .circleci/config.yml > process.yml
|
||||
circleci local execute -c process.yml --job JOB_NAME
|
||||
```
|
596
LICENSE.md
Normal file
596
LICENSE.md
Normal file
@ -0,0 +1,596 @@
|
||||
GNU General Public License
|
||||
==========================
|
||||
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright © 2007 Free Software Foundation, Inc. <<https://fsf.org/>>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this license
|
||||
document, but changing it is not allowed.
|
||||
|
||||
## Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for software and other
|
||||
kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed to take away
|
||||
your freedom to share and change the works. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change all versions of a
|
||||
program--to make sure it remains free software for all its users. We, the Free
|
||||
Software Foundation, use the GNU General Public License for most of our software; it
|
||||
applies also to any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not price. Our General
|
||||
Public Licenses are designed to make sure that you have the freedom to distribute
|
||||
copies of free software (and charge for them if you wish), that you receive source
|
||||
code or can get it if you want it, that you can change the software or use pieces of
|
||||
it in new free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you these rights or
|
||||
asking you to surrender the rights. Therefore, you have certain responsibilities if
|
||||
you distribute copies of the software, or if you modify it: responsibilities to
|
||||
respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether gratis or for a fee,
|
||||
you must pass on to the recipients the same freedoms that you received. You must make
|
||||
sure that they, too, receive or can get the source code. And you must show them these
|
||||
terms so they know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps: **(1)** assert
|
||||
copyright on the software, and **(2)** offer you this License giving you legal permission
|
||||
to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains that there is
|
||||
no warranty for this free software. For both users' and authors' sake, the GPL
|
||||
requires that modified versions be marked as changed, so that their problems will not
|
||||
be attributed erroneously to authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run modified versions of
|
||||
the software inside them, although the manufacturer can do so. This is fundamentally
|
||||
incompatible with the aim of protecting users' freedom to change the software. The
|
||||
systematic pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we have designed
|
||||
this version of the GPL to prohibit the practice for those products. If such problems
|
||||
arise substantially in other domains, we stand ready to extend this provision to
|
||||
those domains in future versions of the GPL, as needed to protect the freedom of
|
||||
users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents. States should
|
||||
not allow patents to restrict development and use of software on general-purpose
|
||||
computers, but in those that do, we wish to avoid the special danger that patents
|
||||
applied to a free program could make it effectively proprietary. To prevent this, the
|
||||
GPL assures that patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and modification follow.
|
||||
|
||||
## TERMS AND CONDITIONS
|
||||
|
||||
### 0. Definitions
|
||||
|
||||
“This License” refers to version 3 of the GNU General Public License.
|
||||
|
||||
“Copyright” also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
“The Program” refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as “you”. “Licensees” and
|
||||
“recipients” may be individuals or organizations.
|
||||
|
||||
To “modify” a work means to copy from or adapt all or part of the work in
|
||||
a fashion requiring copyright permission, other than the making of an exact copy. The
|
||||
resulting work is called a “modified version” of the earlier work or a
|
||||
work “based on” the earlier work.
|
||||
|
||||
A “covered work” means either the unmodified Program or a work based on
|
||||
the Program.
|
||||
|
||||
To “propagate” a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for infringement under
|
||||
applicable copyright law, except executing it on a computer or modifying a private
|
||||
copy. Propagation includes copying, distribution (with or without modification),
|
||||
making available to the public, and in some countries other activities as well.
|
||||
|
||||
To “convey” a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through a computer
|
||||
network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays “Appropriate Legal Notices” to the
|
||||
extent that it includes a convenient and prominently visible feature that **(1)**
|
||||
displays an appropriate copyright notice, and **(2)** tells the user that there is no
|
||||
warranty for the work (except to the extent that warranties are provided), that
|
||||
licensees may convey the work under this License, and how to view a copy of this
|
||||
License. If the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
### 1. Source Code
|
||||
|
||||
The “source code” for a work means the preferred form of the work for
|
||||
making modifications to it. “Object code” means any non-source form of a
|
||||
work.
|
||||
|
||||
A “Standard Interface” means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of interfaces
|
||||
specified for a particular programming language, one that is widely used among
|
||||
developers working in that language.
|
||||
|
||||
The “System Libraries” of an executable work include anything, other than
|
||||
the work as a whole, that **(a)** is included in the normal form of packaging a Major
|
||||
Component, but which is not part of that Major Component, and **(b)** serves only to
|
||||
enable use of the work with that Major Component, or to implement a Standard
|
||||
Interface for which an implementation is available to the public in source code form.
|
||||
A “Major Component”, in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system (if any) on which
|
||||
the executable work runs, or a compiler used to produce the work, or an object code
|
||||
interpreter used to run it.
|
||||
|
||||
The “Corresponding Source” for a work in object code form means all the
|
||||
source code needed to generate, install, and (for an executable work) run the object
|
||||
code and to modify the work, including scripts to control those activities. However,
|
||||
it does not include the work's System Libraries, or general-purpose tools or
|
||||
generally available free programs which are used unmodified in performing those
|
||||
activities but which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for the work, and
|
||||
the source code for shared libraries and dynamically linked subprograms that the work
|
||||
is specifically designed to require, such as by intimate data communication or
|
||||
control flow between those subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users can regenerate
|
||||
automatically from other parts of the Corresponding Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that same work.
|
||||
|
||||
### 2. Basic Permissions
|
||||
|
||||
All rights granted under this License are granted for the term of copyright on the
|
||||
Program, and are irrevocable provided the stated conditions are met. This License
|
||||
explicitly affirms your unlimited permission to run the unmodified Program. The
|
||||
output from running a covered work is covered by this License only if the output,
|
||||
given its content, constitutes a covered work. This License acknowledges your rights
|
||||
of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not convey, without
|
||||
conditions so long as your license otherwise remains in force. You may convey covered
|
||||
works to others for the sole purpose of having them make modifications exclusively
|
||||
for you, or provide you with facilities for running those works, provided that you
|
||||
comply with the terms of this License in conveying all material for which you do not
|
||||
control copyright. Those thus making or running the covered works for you must do so
|
||||
exclusively on your behalf, under your direction and control, on terms that prohibit
|
||||
them from making any copies of your copyrighted material outside their relationship
|
||||
with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under the conditions
|
||||
stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
|
||||
|
||||
### 3. Protecting Users' Legal Rights From Anti-Circumvention Law
|
||||
|
||||
No covered work shall be deemed part of an effective technological measure under any
|
||||
applicable law fulfilling obligations under article 11 of the WIPO copyright treaty
|
||||
adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention
|
||||
of such measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid circumvention of
|
||||
technological measures to the extent such circumvention is effected by exercising
|
||||
rights under this License with respect to the covered work, and you disclaim any
|
||||
intention to limit operation or modification of the work as a means of enforcing,
|
||||
against the work's users, your or third parties' legal rights to forbid circumvention
|
||||
of technological measures.
|
||||
|
||||
### 4. Conveying Verbatim Copies
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you receive it, in any
|
||||
medium, provided that you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice; keep intact all notices stating that this License and
|
||||
any non-permissive terms added in accord with section 7 apply to the code; keep
|
||||
intact all notices of the absence of any warranty; and give all recipients a copy of
|
||||
this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey, and you may offer
|
||||
support or warranty protection for a fee.
|
||||
|
||||
### 5. Conveying Modified Source Versions
|
||||
|
||||
You may convey a work based on the Program, or the modifications to produce it from
|
||||
the Program, in the form of source code under the terms of section 4, provided that
|
||||
you also meet all of these conditions:
|
||||
|
||||
* **a)** The work must carry prominent notices stating that you modified it, and giving a
|
||||
relevant date.
|
||||
* **b)** The work must carry prominent notices stating that it is released under this
|
||||
License and any conditions added under section 7. This requirement modifies the
|
||||
requirement in section 4 to “keep intact all notices”.
|
||||
* **c)** You must license the entire work, as a whole, under this License to anyone who
|
||||
comes into possession of a copy. This License will therefore apply, along with any
|
||||
applicable section 7 additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no permission to license the
|
||||
work in any other way, but it does not invalidate such permission if you have
|
||||
separately received it.
|
||||
* **d)** If the work has interactive user interfaces, each must display Appropriate Legal
|
||||
Notices; however, if the Program has interactive interfaces that do not display
|
||||
Appropriate Legal Notices, your work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent works, which are
|
||||
not by their nature extensions of the covered work, and which are not combined with
|
||||
it such as to form a larger program, in or on a volume of a storage or distribution
|
||||
medium, is called an “aggregate” if the compilation and its resulting
|
||||
copyright are not used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work in an aggregate
|
||||
does not cause this License to apply to the other parts of the aggregate.
|
||||
|
||||
### 6. Conveying Non-Source Forms
|
||||
|
||||
You may convey a covered work in object code form under the terms of sections 4 and
|
||||
5, provided that you also convey the machine-readable Corresponding Source under the
|
||||
terms of this License, in one of these ways:
|
||||
|
||||
* **a)** Convey the object code in, or embodied in, a physical product (including a
|
||||
physical distribution medium), accompanied by the Corresponding Source fixed on a
|
||||
durable physical medium customarily used for software interchange.
|
||||
* **b)** Convey the object code in, or embodied in, a physical product (including a
|
||||
physical distribution medium), accompanied by a written offer, valid for at least
|
||||
three years and valid for as long as you offer spare parts or customer support for
|
||||
that product model, to give anyone who possesses the object code either **(1)** a copy of
|
||||
the Corresponding Source for all the software in the product that is covered by this
|
||||
License, on a durable physical medium customarily used for software interchange, for
|
||||
a price no more than your reasonable cost of physically performing this conveying of
|
||||
source, or **(2)** access to copy the Corresponding Source from a network server at no
|
||||
charge.
|
||||
* **c)** Convey individual copies of the object code with a copy of the written offer to
|
||||
provide the Corresponding Source. This alternative is allowed only occasionally and
|
||||
noncommercially, and only if you received the object code with such an offer, in
|
||||
accord with subsection 6b.
|
||||
* **d)** Convey the object code by offering access from a designated place (gratis or for
|
||||
a charge), and offer equivalent access to the Corresponding Source in the same way
|
||||
through the same place at no further charge. You need not require recipients to copy
|
||||
the Corresponding Source along with the object code. If the place to copy the object
|
||||
code is a network server, the Corresponding Source may be on a different server
|
||||
(operated by you or a third party) that supports equivalent copying facilities,
|
||||
provided you maintain clear directions next to the object code saying where to find
|
||||
the Corresponding Source. Regardless of what server hosts the Corresponding Source,
|
||||
you remain obligated to ensure that it is available for as long as needed to satisfy
|
||||
these requirements.
|
||||
* **e)** Convey the object code using peer-to-peer transmission, provided you inform
|
||||
other peers where the object code and Corresponding Source of the work are being
|
||||
offered to the general public at no charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded from the
|
||||
Corresponding Source as a System Library, need not be included in conveying the
|
||||
object code work.
|
||||
|
||||
A “User Product” is either **(1)** a “consumer product”, which
|
||||
means any tangible personal property which is normally used for personal, family, or
|
||||
household purposes, or **(2)** anything designed or sold for incorporation into a
|
||||
dwelling. In determining whether a product is a consumer product, doubtful cases
|
||||
shall be resolved in favor of coverage. For a particular product received by a
|
||||
particular user, “normally used” refers to a typical or common use of
|
||||
that class of product, regardless of the status of the particular user or of the way
|
||||
in which the particular user actually uses, or expects or is expected to use, the
|
||||
product. A product is a consumer product regardless of whether the product has
|
||||
substantial commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
“Installation Information” for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install and execute
|
||||
modified versions of a covered work in that User Product from a modified version of
|
||||
its Corresponding Source. The information must suffice to ensure that the continued
|
||||
functioning of the modified object code is in no case prevented or interfered with
|
||||
solely because modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or specifically for
|
||||
use in, a User Product, and the conveying occurs as part of a transaction in which
|
||||
the right of possession and use of the User Product is transferred to the recipient
|
||||
in perpetuity or for a fixed term (regardless of how the transaction is
|
||||
characterized), the Corresponding Source conveyed under this section must be
|
||||
accompanied by the Installation Information. But this requirement does not apply if
|
||||
neither you nor any third party retains the ability to install modified object code
|
||||
on the User Product (for example, the work has been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a requirement to
|
||||
continue to provide support service, warranty, or updates for a work that has been
|
||||
modified or installed by the recipient, or for the User Product in which it has been
|
||||
modified or installed. Access to a network may be denied when the modification itself
|
||||
materially and adversely affects the operation of the network or violates the rules
|
||||
and protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided, in accord with
|
||||
this section must be in a format that is publicly documented (and with an
|
||||
implementation available to the public in source code form), and must require no
|
||||
special password or key for unpacking, reading or copying.
|
||||
|
||||
### 7. Additional Terms
|
||||
|
||||
“Additional permissions” are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions. Additional
|
||||
permissions that are applicable to the entire Program shall be treated as though they
|
||||
were included in this License, to the extent that they are valid under applicable
|
||||
law. If additional permissions apply only to part of the Program, that part may be
|
||||
used separately under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option remove any
|
||||
additional permissions from that copy, or from any part of it. (Additional
|
||||
permissions may be written to require their own removal in certain cases when you
|
||||
modify the work.) You may place additional permissions on material, added by you to a
|
||||
covered work, for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you add to a
|
||||
covered work, you may (if authorized by the copyright holders of that material)
|
||||
supplement the terms of this License with terms:
|
||||
|
||||
* **a)** Disclaiming warranty or limiting liability differently from the terms of
|
||||
sections 15 and 16 of this License; or
|
||||
* **b)** Requiring preservation of specified reasonable legal notices or author
|
||||
attributions in that material or in the Appropriate Legal Notices displayed by works
|
||||
containing it; or
|
||||
* **c)** Prohibiting misrepresentation of the origin of that material, or requiring that
|
||||
modified versions of such material be marked in reasonable ways as different from the
|
||||
original version; or
|
||||
* **d)** Limiting the use for publicity purposes of names of licensors or authors of the
|
||||
material; or
|
||||
* **e)** Declining to grant rights under trademark law for use of some trade names,
|
||||
trademarks, or service marks; or
|
||||
* **f)** Requiring indemnification of licensors and authors of that material by anyone
|
||||
who conveys the material (or modified versions of it) with contractual assumptions of
|
||||
liability to the recipient, for any liability that these contractual assumptions
|
||||
directly impose on those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered “further
|
||||
restrictions” within the meaning of section 10. If the Program as you received
|
||||
it, or any part of it, contains a notice stating that it is governed by this License
|
||||
along with a term that is a further restriction, you may remove that term. If a
|
||||
license document contains a further restriction but permits relicensing or conveying
|
||||
under this License, you may add to a covered work material governed by the terms of
|
||||
that license document, provided that the further restriction does not survive such
|
||||
relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you must place, in
|
||||
the relevant source files, a statement of the additional terms that apply to those
|
||||
files, or a notice indicating where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the form of a
|
||||
separately written license, or stated as exceptions; the above requirements apply
|
||||
either way.
|
||||
|
||||
### 8. Termination
|
||||
|
||||
You may not propagate or modify a covered work except as expressly provided under
|
||||
this License. Any attempt otherwise to propagate or modify it is void, and will
|
||||
automatically terminate your rights under this License (including any patent licenses
|
||||
granted under the third paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your license from a
|
||||
particular copyright holder is reinstated **(a)** provisionally, unless and until the
|
||||
copyright holder explicitly and finally terminates your license, and **(b)** permanently,
|
||||
if the copyright holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is reinstated permanently
|
||||
if the copyright holder notifies you of the violation by some reasonable means, this
|
||||
is the first time you have received notice of violation of this License (for any
|
||||
work) from that copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the licenses of
|
||||
parties who have received copies or rights from you under this License. If your
|
||||
rights have been terminated and not permanently reinstated, you do not qualify to
|
||||
receive new licenses for the same material under section 10.
|
||||
|
||||
### 9. Acceptance Not Required for Having Copies
|
||||
|
||||
You are not required to accept this License in order to receive or run a copy of the
|
||||
Program. Ancillary propagation of a covered work occurring solely as a consequence of
|
||||
using peer-to-peer transmission to receive a copy likewise does not require
|
||||
acceptance. However, nothing other than this License grants you permission to
|
||||
propagate or modify any covered work. These actions infringe copyright if you do not
|
||||
accept this License. Therefore, by modifying or propagating a covered work, you
|
||||
indicate your acceptance of this License to do so.
|
||||
|
||||
### 10. Automatic Licensing of Downstream Recipients
|
||||
|
||||
Each time you convey a covered work, the recipient automatically receives a license
|
||||
from the original licensors, to run, modify and propagate that work, subject to this
|
||||
License. You are not responsible for enforcing compliance by third parties with this
|
||||
License.
|
||||
|
||||
An “entity transaction” is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an organization, or
|
||||
merging organizations. If propagation of a covered work results from an entity
|
||||
transaction, each party to that transaction who receives a copy of the work also
|
||||
receives whatever licenses to the work the party's predecessor in interest had or
|
||||
could give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if the predecessor
|
||||
has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the rights granted or
|
||||
affirmed under this License. For example, you may not impose a license fee, royalty,
|
||||
or other charge for exercise of rights granted under this License, and you may not
|
||||
initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that any patent claim is infringed by making, using, selling, offering for sale, or
|
||||
importing the Program or any portion of it.
|
||||
|
||||
### 11. Patents
|
||||
|
||||
A “contributor” is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The work thus
|
||||
licensed is called the contributor's “contributor version”.
|
||||
|
||||
A contributor's “essential patent claims” are all patent claims owned or
|
||||
controlled by the contributor, whether already acquired or hereafter acquired, that
|
||||
would be infringed by some manner, permitted by this License, of making, using, or
|
||||
selling its contributor version, but do not include claims that would be infringed
|
||||
only as a consequence of further modification of the contributor version. For
|
||||
purposes of this definition, “control” includes the right to grant patent
|
||||
sublicenses in a manner consistent with the requirements of this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free patent license
|
||||
under the contributor's essential patent claims, to make, use, sell, offer for sale,
|
||||
import and otherwise run, modify and propagate the contents of its contributor
|
||||
version.
|
||||
|
||||
In the following three paragraphs, a “patent license” is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent (such as an
|
||||
express permission to practice a patent or covenant not to sue for patent
|
||||
infringement). To “grant” such a patent license to a party means to make
|
||||
such an agreement or commitment not to enforce a patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license, and the
|
||||
Corresponding Source of the work is not available for anyone to copy, free of charge
|
||||
and under the terms of this License, through a publicly available network server or
|
||||
other readily accessible means, then you must either **(1)** cause the Corresponding
|
||||
Source to be so available, or **(2)** arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or **(3)** arrange, in a manner consistent with
|
||||
the requirements of this License, to extend the patent license to downstream
|
||||
recipients. “Knowingly relying” means you have actual knowledge that, but
|
||||
for the patent license, your conveying the covered work in a country, or your
|
||||
recipient's use of the covered work in a country, would infringe one or more
|
||||
identifiable patents in that country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or arrangement, you
|
||||
convey, or propagate by procuring conveyance of, a covered work, and grant a patent
|
||||
license to some of the parties receiving the covered work authorizing them to use,
|
||||
propagate, modify or convey a specific copy of the covered work, then the patent
|
||||
license you grant is automatically extended to all recipients of the covered work and
|
||||
works based on it.
|
||||
|
||||
A patent license is “discriminatory” if it does not include within the
|
||||
scope of its coverage, prohibits the exercise of, or is conditioned on the
|
||||
non-exercise of one or more of the rights that are specifically granted under this
|
||||
License. You may not convey a covered work if you are a party to an arrangement with
|
||||
a third party that is in the business of distributing software, under which you make
|
||||
payment to the third party based on the extent of your activity of conveying the
|
||||
work, and under which the third party grants, to any of the parties who would receive
|
||||
the covered work from you, a discriminatory patent license **(a)** in connection with
|
||||
copies of the covered work conveyed by you (or copies made from those copies), or **(b)**
|
||||
primarily for and in connection with specific products or compilations that contain
|
||||
the covered work, unless you entered into that arrangement, or that patent license
|
||||
was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting any implied
|
||||
license or other defenses to infringement that may otherwise be available to you
|
||||
under applicable patent law.
|
||||
|
||||
### 12. No Surrender of Others' Freedom
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or otherwise)
|
||||
that contradict the conditions of this License, they do not excuse you from the
|
||||
conditions of this License. If you cannot convey a covered work so as to satisfy
|
||||
simultaneously your obligations under this License and any other pertinent
|
||||
obligations, then as a consequence you may not convey it at all. For example, if you
|
||||
agree to terms that obligate you to collect a royalty for further conveying from
|
||||
those to whom you convey the Program, the only way you could satisfy both those terms
|
||||
and this License would be to refrain entirely from conveying the Program.
|
||||
|
||||
### 13. Use with the GNU Affero General Public License
|
||||
|
||||
Notwithstanding any other provision of this License, you have permission to link or
|
||||
combine any covered work with a work licensed under version 3 of the GNU Affero
|
||||
General Public License into a single combined work, and to convey the resulting work.
|
||||
The terms of this License will continue to apply to the part which is the covered
|
||||
work, but the special requirements of the GNU Affero General Public License, section
|
||||
13, concerning interaction through a network will apply to the combination as such.
|
||||
|
||||
### 14. Revised Versions of this License
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of the GNU
|
||||
General Public License from time to time. Such new versions will be similar in spirit
|
||||
to the present version, but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program specifies that
|
||||
a certain numbered version of the GNU General Public License “or any later
|
||||
version” applies to it, you have the option of following the terms and
|
||||
conditions either of that numbered version or of any later version published by the
|
||||
Free Software Foundation. If the Program does not specify a version number of the GNU
|
||||
General Public License, you may choose any version ever published by the Free
|
||||
Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future versions of the GNU
|
||||
General Public License can be used, that proxy's public statement of acceptance of a
|
||||
version permanently authorizes you to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different permissions. However, no
|
||||
additional obligations are imposed on any author or copyright holder as a result of
|
||||
your choosing to follow a later version.
|
||||
|
||||
### 15. Disclaimer of Warranty
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE
|
||||
QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
|
||||
DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
### 16. Limitation of Liability
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
|
||||
COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS
|
||||
PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
|
||||
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE
|
||||
OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
|
||||
WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
### 17. Interpretation of Sections 15 and 16
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided above cannot be
|
||||
given local legal effect according to their terms, reviewing courts shall apply local
|
||||
law that most closely approximates an absolute waiver of all civil liability in
|
||||
connection with the Program, unless a warranty or assumption of liability accompanies
|
||||
a copy of the Program in return for a fee.
|
||||
|
||||
_END OF TERMS AND CONDITIONS_
|
||||
|
||||
## How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest possible use to
|
||||
the public, the best way to achieve this is to make it free software which everyone
|
||||
can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest to attach them
|
||||
to the start of each source file to most effectively state the exclusion of warranty;
|
||||
and each file should have at least the “copyright” line and a pointer to
|
||||
where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type 'show c' for details.
|
||||
|
||||
The hypothetical commands `show w` and `show c` should show the appropriate parts of
|
||||
the General Public License. Of course, your program's commands might be different;
|
||||
for a GUI interface, you would use an “about box”.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school, if any, to
|
||||
sign a “copyright disclaimer” for the program, if necessary. For more
|
||||
information on this, and how to apply and follow the GNU GPL, see
|
||||
<<http://www.gnu.org/licenses/>>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may consider it
|
||||
more useful to permit linking proprietary applications with the library. If this is
|
||||
what you want to do, use the GNU Lesser General Public License instead of this
|
||||
License. But first, please read
|
||||
<<http://www.gnu.org/philosophy/why-not-lgpl.html>>.
|
13
README.md
13
README.md
@ -1,5 +1,5 @@
|
||||
<p align="center">
|
||||
<img width="1412" src="./.resources/leo.png">
|
||||
<img width="1412" src="https://cdn.aleo.org/leo/banner.png">
|
||||
</p>
|
||||
|
||||
<h1 align="center">The Leo Programming Language</h1>
|
||||
@ -7,7 +7,8 @@
|
||||
<p align="center">
|
||||
<a href="https://github.com/AleoHQ/leo/actions"><img src="https://github.com/AleoHQ/leo/workflows/CI/badge.svg"></a>
|
||||
<a href="https://codecov.io/gh/AleoHQ/leo"><img src="https://codecov.io/gh/AleoHQ/leo/branch/master/graph/badge.svg?token=S6MWO60SYL"/></a>
|
||||
<a href="https://discord.gg/TTexWvt"><img src="https://img.shields.io/discord/700454073459015690?logo=discord"/></a>
|
||||
<a href="https://app.bors.tech/repositories/31738"><img src="https://bors.tech/images/badge_small.svg" alt="Bors enabled"></a>
|
||||
<a href="https://discord.gg/5v2ynrw2ds"><img src="https://img.shields.io/discord/700454073459015690?logo=discord"/></a>
|
||||
</p>
|
||||
|
||||
Leo is a functional, statically-typed programming language built for writing private applications.
|
||||
@ -26,6 +27,7 @@ Leo is a functional, statically-typed programming language built for writing pri
|
||||
|
||||
|
||||
## 1. Overview
|
||||
|
||||
Welcome to the Leo programming language.
|
||||
|
||||
Leo provides a high-level language that abstracts low-level cryptographic concepts and makes it easy to
|
||||
@ -93,9 +95,9 @@ This will generate an executable under the `./target/release` directory. To run
|
||||
Use the Leo CLI to create a new project
|
||||
|
||||
```bash
|
||||
# create a new `hello_world` Leo project
|
||||
leo new hello_world
|
||||
cd hello_world
|
||||
# create a new `hello-world` Leo project
|
||||
leo new hello-world
|
||||
cd hello-world
|
||||
|
||||
# build & setup & prove & verify
|
||||
leo run
|
||||
@ -111,6 +113,7 @@ Congratulations! You've just run your first Leo program.
|
||||
|
||||
* [Hello World - Next Steps](https://developer.aleo.org/developer/getting_started/hello_world)
|
||||
* [Leo Language Documentation](https://developer.aleo.org/developer/language/layout)
|
||||
* [Leo ABNF Grammar](./grammar/README.md)
|
||||
* [Leo CLI Documentation](https://developer.aleo.org/developer/cli/new)
|
||||
* [Homepage](https://developer.aleo.org/developer/getting_started/overview)
|
||||
|
||||
|
17
SECURITY.md
Normal file
17
SECURITY.md
Normal file
@ -0,0 +1,17 @@
|
||||
# Security
|
||||
|
||||
The following describes our procedure for addressing major and minor security concerns in Leo.
|
||||
|
||||
## Testnet I
|
||||
|
||||
As Aleo is currently in the prototype stage and does not operate a platform intended for production use,
|
||||
our security procedures are designed to promote public disclosure and quick security resolution.
|
||||
|
||||
In preparation for the production stage, we will release new security guidelines and
|
||||
issue new procedures for addressing the disclosure of sensitive security vulnerabilities.
|
||||
|
||||
### Reporting a Bug
|
||||
|
||||
During Testnet I, all software bugs should be reported by filing a Github issue.
|
||||
|
||||
If you are unsure and would like to reach out to us directly, please email security \_at\_ aleo.org to elaborate on the issue.
|
25
asg-passes/Cargo.toml
Normal file
25
asg-passes/Cargo.toml
Normal file
@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "leo-asg-passes"
|
||||
version = "1.4.0"
|
||||
authors = [ "The Aleo Team <hello@aleo.org>" ]
|
||||
description = "The Leo programming language"
|
||||
homepage = "https://aleo.org"
|
||||
repository = "https://github.com/AleoHQ/leo"
|
||||
keywords = [
|
||||
"aleo",
|
||||
"cryptography",
|
||||
"leo",
|
||||
"programming-language",
|
||||
"zero-knowledge"
|
||||
]
|
||||
categories = [ "cryptography::cryptocurrencies", "web-programming" ]
|
||||
include = [ "Cargo.toml", "src", "README.md", "LICENSE.md" ]
|
||||
license = "GPL-3.0"
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies.leo-asg]
|
||||
path = "../asg"
|
||||
version = "1.4.0"
|
54
asg-passes/src/constant_folding/mod.rs
Normal file
54
asg-passes/src/constant_folding/mod.rs
Normal file
@ -0,0 +1,54 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
use leo_asg::*;
|
||||
|
||||
pub struct ConstantFolding<'a, 'b> {
|
||||
program: &'b Program<'a>,
|
||||
}
|
||||
|
||||
impl<'a, 'b> ExpressionVisitor<'a> for ConstantFolding<'a, 'b> {
|
||||
fn visit_expression(&mut self, input: &Cell<&Expression<'a>>) -> VisitResult {
|
||||
let expr = input.get();
|
||||
if let Some(const_value) = expr.const_value() {
|
||||
let folded_expr = Expression::Constant(Constant {
|
||||
parent: Cell::new(expr.get_parent()),
|
||||
span: expr.span().cloned(),
|
||||
value: const_value,
|
||||
});
|
||||
let folded_expr = self.program.context.alloc_expression(folded_expr);
|
||||
input.set(folded_expr);
|
||||
VisitResult::SkipChildren
|
||||
} else {
|
||||
VisitResult::VisitChildren
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> StatementVisitor<'a> for ConstantFolding<'a, 'b> {}
|
||||
|
||||
impl<'a, 'b> ProgramVisitor<'a> for ConstantFolding<'a, 'b> {}
|
||||
|
||||
impl<'a, 'b> AsgPass<'a> for ConstantFolding<'a, 'b> {
|
||||
fn do_pass(asg: Program<'a>) -> Result<Program<'a>, FormattedError> {
|
||||
let pass = ConstantFolding { program: &asg };
|
||||
let mut director = VisitorDirector::new(pass);
|
||||
director.visit_program(&asg).ok();
|
||||
Ok(asg)
|
||||
}
|
||||
}
|
73
asg-passes/src/dead_code_elimination/mod.rs
Normal file
73
asg-passes/src/dead_code_elimination/mod.rs
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
use leo_asg::*;
|
||||
|
||||
pub struct DeadCodeElimination {}
|
||||
|
||||
impl<'a> ReconstructingReducerExpression<'a> for DeadCodeElimination {}
|
||||
|
||||
impl<'a> ReconstructingReducerProgram<'a> for DeadCodeElimination {}
|
||||
|
||||
impl<'a> ReconstructingReducerStatement<'a> for DeadCodeElimination {
|
||||
///
|
||||
/// Removes dead code inside a false conditional statement block.
|
||||
///
|
||||
fn reduce_statement_alloc(
|
||||
&mut self,
|
||||
context: AsgContext<'a>,
|
||||
_input: &'a Statement<'a>,
|
||||
value: Statement<'a>,
|
||||
) -> &'a Statement<'a> {
|
||||
match &value {
|
||||
Statement::Conditional(conditional) => match conditional.condition.get().const_value() {
|
||||
Some(ConstValue::Boolean(true)) => conditional.result.get(),
|
||||
Some(ConstValue::Boolean(false)) => {
|
||||
if let Some(if_false) = conditional.next.get() {
|
||||
if_false
|
||||
} else {
|
||||
context.alloc_statement(Statement::Empty(conditional.span.clone()))
|
||||
}
|
||||
}
|
||||
_ => context.alloc_statement(value),
|
||||
},
|
||||
_ => context.alloc_statement(value),
|
||||
}
|
||||
}
|
||||
|
||||
fn reduce_block(&mut self, input: BlockStatement<'a>, mut statements: Vec<&'a Statement<'a>>) -> Statement<'a> {
|
||||
let first_return = statements.iter().position(|x| matches!(x, Statement::Return(_)));
|
||||
if let Some(first_return) = first_return {
|
||||
statements.truncate(first_return + 1);
|
||||
}
|
||||
Statement::Block(BlockStatement {
|
||||
parent: input.parent,
|
||||
span: input.span,
|
||||
statements: statements.into_iter().map(Cell::new).collect(),
|
||||
scope: input.scope,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsgPass<'a> for DeadCodeElimination {
|
||||
fn do_pass(asg: Program<'a>) -> Result<Program<'a>, FormattedError> {
|
||||
let pass = DeadCodeElimination {};
|
||||
let mut director = ReconstructingDirector::new(asg.context, pass);
|
||||
Ok(director.reduce_program(asg))
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2020 Aleo Systems Inc.
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
@ -14,8 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
pub mod annotated_definition;
|
||||
pub use annotated_definition::*;
|
||||
pub mod constant_folding;
|
||||
pub use constant_folding::*;
|
||||
|
||||
pub mod definition;
|
||||
pub use definition::*;
|
||||
pub mod dead_code_elimination;
|
||||
pub use dead_code_elimination::*;
|
@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "leo-type-inference"
|
||||
version = "1.0.8"
|
||||
name = "leo-asg"
|
||||
version = "1.4.0"
|
||||
authors = [ "The Aleo Team <hello@aleo.org>" ]
|
||||
description = "Checks that a program is correct using type inference"
|
||||
description = "ASG of the Leo programming language"
|
||||
homepage = "https://aleo.org"
|
||||
repository = "https://github.com/AleoHQ/leo"
|
||||
keywords = [
|
||||
@ -17,31 +17,34 @@ include = [ "Cargo.toml", "src", "README.md", "LICENSE.md" ]
|
||||
license = "GPL-3.0"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies.leo-ast]
|
||||
path = "../ast"
|
||||
version = "1.0.8"
|
||||
|
||||
[dependencies.leo-imports]
|
||||
path = "../imports"
|
||||
version = "1.0.8"
|
||||
|
||||
[dependencies.leo-grammar]
|
||||
path = "../grammar"
|
||||
version = "1.0.8"
|
||||
|
||||
[dependencies.leo-symbol-table]
|
||||
path = "../symbol-table"
|
||||
version = "1.0.8"
|
||||
|
||||
[dependencies.indexmap]
|
||||
version = "1.6.1"
|
||||
features = [ "serde-1" ]
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.serde_json]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
[dependencies.indexmap]
|
||||
version = "1.6"
|
||||
|
||||
[dependencies.thiserror]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.leo-ast]
|
||||
version = "1.4.0"
|
||||
path = "../ast"
|
||||
|
||||
[dependencies.leo-parser]
|
||||
version = "1.4.0"
|
||||
path = "../parser"
|
||||
|
||||
[dependencies.num-bigint]
|
||||
version = "0.4"
|
||||
|
||||
[dependencies.typed-arena]
|
||||
version = "2.0"
|
||||
|
||||
[dependencies.tendril]
|
||||
version = "0.4"
|
||||
|
||||
[dev-dependencies.criterion]
|
||||
version = "0.3"
|
596
asg/LICENSE.md
Normal file
596
asg/LICENSE.md
Normal file
@ -0,0 +1,596 @@
|
||||
GNU General Public License
|
||||
==========================
|
||||
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright © 2007 Free Software Foundation, Inc. <<https://fsf.org/>>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this license
|
||||
document, but changing it is not allowed.
|
||||
|
||||
## Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for software and other
|
||||
kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed to take away
|
||||
your freedom to share and change the works. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change all versions of a
|
||||
program--to make sure it remains free software for all its users. We, the Free
|
||||
Software Foundation, use the GNU General Public License for most of our software; it
|
||||
applies also to any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not price. Our General
|
||||
Public Licenses are designed to make sure that you have the freedom to distribute
|
||||
copies of free software (and charge for them if you wish), that you receive source
|
||||
code or can get it if you want it, that you can change the software or use pieces of
|
||||
it in new free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you these rights or
|
||||
asking you to surrender the rights. Therefore, you have certain responsibilities if
|
||||
you distribute copies of the software, or if you modify it: responsibilities to
|
||||
respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether gratis or for a fee,
|
||||
you must pass on to the recipients the same freedoms that you received. You must make
|
||||
sure that they, too, receive or can get the source code. And you must show them these
|
||||
terms so they know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps: **(1)** assert
|
||||
copyright on the software, and **(2)** offer you this License giving you legal permission
|
||||
to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains that there is
|
||||
no warranty for this free software. For both users' and authors' sake, the GPL
|
||||
requires that modified versions be marked as changed, so that their problems will not
|
||||
be attributed erroneously to authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run modified versions of
|
||||
the software inside them, although the manufacturer can do so. This is fundamentally
|
||||
incompatible with the aim of protecting users' freedom to change the software. The
|
||||
systematic pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we have designed
|
||||
this version of the GPL to prohibit the practice for those products. If such problems
|
||||
arise substantially in other domains, we stand ready to extend this provision to
|
||||
those domains in future versions of the GPL, as needed to protect the freedom of
|
||||
users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents. States should
|
||||
not allow patents to restrict development and use of software on general-purpose
|
||||
computers, but in those that do, we wish to avoid the special danger that patents
|
||||
applied to a free program could make it effectively proprietary. To prevent this, the
|
||||
GPL assures that patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and modification follow.
|
||||
|
||||
## TERMS AND CONDITIONS
|
||||
|
||||
### 0. Definitions
|
||||
|
||||
“This License” refers to version 3 of the GNU General Public License.
|
||||
|
||||
“Copyright” also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
“The Program” refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as “you”. “Licensees” and
|
||||
“recipients” may be individuals or organizations.
|
||||
|
||||
To “modify” a work means to copy from or adapt all or part of the work in
|
||||
a fashion requiring copyright permission, other than the making of an exact copy. The
|
||||
resulting work is called a “modified version” of the earlier work or a
|
||||
work “based on” the earlier work.
|
||||
|
||||
A “covered work” means either the unmodified Program or a work based on
|
||||
the Program.
|
||||
|
||||
To “propagate” a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for infringement under
|
||||
applicable copyright law, except executing it on a computer or modifying a private
|
||||
copy. Propagation includes copying, distribution (with or without modification),
|
||||
making available to the public, and in some countries other activities as well.
|
||||
|
||||
To “convey” a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through a computer
|
||||
network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays “Appropriate Legal Notices” to the
|
||||
extent that it includes a convenient and prominently visible feature that **(1)**
|
||||
displays an appropriate copyright notice, and **(2)** tells the user that there is no
|
||||
warranty for the work (except to the extent that warranties are provided), that
|
||||
licensees may convey the work under this License, and how to view a copy of this
|
||||
License. If the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
### 1. Source Code
|
||||
|
||||
The “source code” for a work means the preferred form of the work for
|
||||
making modifications to it. “Object code” means any non-source form of a
|
||||
work.
|
||||
|
||||
A “Standard Interface” means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of interfaces
|
||||
specified for a particular programming language, one that is widely used among
|
||||
developers working in that language.
|
||||
|
||||
The “System Libraries” of an executable work include anything, other than
|
||||
the work as a whole, that **(a)** is included in the normal form of packaging a Major
|
||||
Component, but which is not part of that Major Component, and **(b)** serves only to
|
||||
enable use of the work with that Major Component, or to implement a Standard
|
||||
Interface for which an implementation is available to the public in source code form.
|
||||
A “Major Component”, in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system (if any) on which
|
||||
the executable work runs, or a compiler used to produce the work, or an object code
|
||||
interpreter used to run it.
|
||||
|
||||
The “Corresponding Source” for a work in object code form means all the
|
||||
source code needed to generate, install, and (for an executable work) run the object
|
||||
code and to modify the work, including scripts to control those activities. However,
|
||||
it does not include the work's System Libraries, or general-purpose tools or
|
||||
generally available free programs which are used unmodified in performing those
|
||||
activities but which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for the work, and
|
||||
the source code for shared libraries and dynamically linked subprograms that the work
|
||||
is specifically designed to require, such as by intimate data communication or
|
||||
control flow between those subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users can regenerate
|
||||
automatically from other parts of the Corresponding Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that same work.
|
||||
|
||||
### 2. Basic Permissions
|
||||
|
||||
All rights granted under this License are granted for the term of copyright on the
|
||||
Program, and are irrevocable provided the stated conditions are met. This License
|
||||
explicitly affirms your unlimited permission to run the unmodified Program. The
|
||||
output from running a covered work is covered by this License only if the output,
|
||||
given its content, constitutes a covered work. This License acknowledges your rights
|
||||
of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not convey, without
|
||||
conditions so long as your license otherwise remains in force. You may convey covered
|
||||
works to others for the sole purpose of having them make modifications exclusively
|
||||
for you, or provide you with facilities for running those works, provided that you
|
||||
comply with the terms of this License in conveying all material for which you do not
|
||||
control copyright. Those thus making or running the covered works for you must do so
|
||||
exclusively on your behalf, under your direction and control, on terms that prohibit
|
||||
them from making any copies of your copyrighted material outside their relationship
|
||||
with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under the conditions
|
||||
stated below. Sublicensing is not allowed; section 10 makes it unnecessary.
|
||||
|
||||
### 3. Protecting Users' Legal Rights From Anti-Circumvention Law
|
||||
|
||||
No covered work shall be deemed part of an effective technological measure under any
|
||||
applicable law fulfilling obligations under article 11 of the WIPO copyright treaty
|
||||
adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention
|
||||
of such measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid circumvention of
|
||||
technological measures to the extent such circumvention is effected by exercising
|
||||
rights under this License with respect to the covered work, and you disclaim any
|
||||
intention to limit operation or modification of the work as a means of enforcing,
|
||||
against the work's users, your or third parties' legal rights to forbid circumvention
|
||||
of technological measures.
|
||||
|
||||
### 4. Conveying Verbatim Copies
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you receive it, in any
|
||||
medium, provided that you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice; keep intact all notices stating that this License and
|
||||
any non-permissive terms added in accord with section 7 apply to the code; keep
|
||||
intact all notices of the absence of any warranty; and give all recipients a copy of
|
||||
this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey, and you may offer
|
||||
support or warranty protection for a fee.
|
||||
|
||||
### 5. Conveying Modified Source Versions
|
||||
|
||||
You may convey a work based on the Program, or the modifications to produce it from
|
||||
the Program, in the form of source code under the terms of section 4, provided that
|
||||
you also meet all of these conditions:
|
||||
|
||||
* **a)** The work must carry prominent notices stating that you modified it, and giving a
|
||||
relevant date.
|
||||
* **b)** The work must carry prominent notices stating that it is released under this
|
||||
License and any conditions added under section 7. This requirement modifies the
|
||||
requirement in section 4 to “keep intact all notices”.
|
||||
* **c)** You must license the entire work, as a whole, under this License to anyone who
|
||||
comes into possession of a copy. This License will therefore apply, along with any
|
||||
applicable section 7 additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no permission to license the
|
||||
work in any other way, but it does not invalidate such permission if you have
|
||||
separately received it.
|
||||
* **d)** If the work has interactive user interfaces, each must display Appropriate Legal
|
||||
Notices; however, if the Program has interactive interfaces that do not display
|
||||
Appropriate Legal Notices, your work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent works, which are
|
||||
not by their nature extensions of the covered work, and which are not combined with
|
||||
it such as to form a larger program, in or on a volume of a storage or distribution
|
||||
medium, is called an “aggregate” if the compilation and its resulting
|
||||
copyright are not used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work in an aggregate
|
||||
does not cause this License to apply to the other parts of the aggregate.
|
||||
|
||||
### 6. Conveying Non-Source Forms
|
||||
|
||||
You may convey a covered work in object code form under the terms of sections 4 and
|
||||
5, provided that you also convey the machine-readable Corresponding Source under the
|
||||
terms of this License, in one of these ways:
|
||||
|
||||
* **a)** Convey the object code in, or embodied in, a physical product (including a
|
||||
physical distribution medium), accompanied by the Corresponding Source fixed on a
|
||||
durable physical medium customarily used for software interchange.
|
||||
* **b)** Convey the object code in, or embodied in, a physical product (including a
|
||||
physical distribution medium), accompanied by a written offer, valid for at least
|
||||
three years and valid for as long as you offer spare parts or customer support for
|
||||
that product model, to give anyone who possesses the object code either **(1)** a copy of
|
||||
the Corresponding Source for all the software in the product that is covered by this
|
||||
License, on a durable physical medium customarily used for software interchange, for
|
||||
a price no more than your reasonable cost of physically performing this conveying of
|
||||
source, or **(2)** access to copy the Corresponding Source from a network server at no
|
||||
charge.
|
||||
* **c)** Convey individual copies of the object code with a copy of the written offer to
|
||||
provide the Corresponding Source. This alternative is allowed only occasionally and
|
||||
noncommercially, and only if you received the object code with such an offer, in
|
||||
accord with subsection 6b.
|
||||
* **d)** Convey the object code by offering access from a designated place (gratis or for
|
||||
a charge), and offer equivalent access to the Corresponding Source in the same way
|
||||
through the same place at no further charge. You need not require recipients to copy
|
||||
the Corresponding Source along with the object code. If the place to copy the object
|
||||
code is a network server, the Corresponding Source may be on a different server
|
||||
(operated by you or a third party) that supports equivalent copying facilities,
|
||||
provided you maintain clear directions next to the object code saying where to find
|
||||
the Corresponding Source. Regardless of what server hosts the Corresponding Source,
|
||||
you remain obligated to ensure that it is available for as long as needed to satisfy
|
||||
these requirements.
|
||||
* **e)** Convey the object code using peer-to-peer transmission, provided you inform
|
||||
other peers where the object code and Corresponding Source of the work are being
|
||||
offered to the general public at no charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded from the
|
||||
Corresponding Source as a System Library, need not be included in conveying the
|
||||
object code work.
|
||||
|
||||
A “User Product” is either **(1)** a “consumer product”, which
|
||||
means any tangible personal property which is normally used for personal, family, or
|
||||
household purposes, or **(2)** anything designed or sold for incorporation into a
|
||||
dwelling. In determining whether a product is a consumer product, doubtful cases
|
||||
shall be resolved in favor of coverage. For a particular product received by a
|
||||
particular user, “normally used” refers to a typical or common use of
|
||||
that class of product, regardless of the status of the particular user or of the way
|
||||
in which the particular user actually uses, or expects or is expected to use, the
|
||||
product. A product is a consumer product regardless of whether the product has
|
||||
substantial commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
“Installation Information” for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install and execute
|
||||
modified versions of a covered work in that User Product from a modified version of
|
||||
its Corresponding Source. The information must suffice to ensure that the continued
|
||||
functioning of the modified object code is in no case prevented or interfered with
|
||||
solely because modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or specifically for
|
||||
use in, a User Product, and the conveying occurs as part of a transaction in which
|
||||
the right of possession and use of the User Product is transferred to the recipient
|
||||
in perpetuity or for a fixed term (regardless of how the transaction is
|
||||
characterized), the Corresponding Source conveyed under this section must be
|
||||
accompanied by the Installation Information. But this requirement does not apply if
|
||||
neither you nor any third party retains the ability to install modified object code
|
||||
on the User Product (for example, the work has been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a requirement to
|
||||
continue to provide support service, warranty, or updates for a work that has been
|
||||
modified or installed by the recipient, or for the User Product in which it has been
|
||||
modified or installed. Access to a network may be denied when the modification itself
|
||||
materially and adversely affects the operation of the network or violates the rules
|
||||
and protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided, in accord with
|
||||
this section must be in a format that is publicly documented (and with an
|
||||
implementation available to the public in source code form), and must require no
|
||||
special password or key for unpacking, reading or copying.
|
||||
|
||||
### 7. Additional Terms
|
||||
|
||||
“Additional permissions” are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions. Additional
|
||||
permissions that are applicable to the entire Program shall be treated as though they
|
||||
were included in this License, to the extent that they are valid under applicable
|
||||
law. If additional permissions apply only to part of the Program, that part may be
|
||||
used separately under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option remove any
|
||||
additional permissions from that copy, or from any part of it. (Additional
|
||||
permissions may be written to require their own removal in certain cases when you
|
||||
modify the work.) You may place additional permissions on material, added by you to a
|
||||
covered work, for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you add to a
|
||||
covered work, you may (if authorized by the copyright holders of that material)
|
||||
supplement the terms of this License with terms:
|
||||
|
||||
* **a)** Disclaiming warranty or limiting liability differently from the terms of
|
||||
sections 15 and 16 of this License; or
|
||||
* **b)** Requiring preservation of specified reasonable legal notices or author
|
||||
attributions in that material or in the Appropriate Legal Notices displayed by works
|
||||
containing it; or
|
||||
* **c)** Prohibiting misrepresentation of the origin of that material, or requiring that
|
||||
modified versions of such material be marked in reasonable ways as different from the
|
||||
original version; or
|
||||
* **d)** Limiting the use for publicity purposes of names of licensors or authors of the
|
||||
material; or
|
||||
* **e)** Declining to grant rights under trademark law for use of some trade names,
|
||||
trademarks, or service marks; or
|
||||
* **f)** Requiring indemnification of licensors and authors of that material by anyone
|
||||
who conveys the material (or modified versions of it) with contractual assumptions of
|
||||
liability to the recipient, for any liability that these contractual assumptions
|
||||
directly impose on those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered “further
|
||||
restrictions” within the meaning of section 10. If the Program as you received
|
||||
it, or any part of it, contains a notice stating that it is governed by this License
|
||||
along with a term that is a further restriction, you may remove that term. If a
|
||||
license document contains a further restriction but permits relicensing or conveying
|
||||
under this License, you may add to a covered work material governed by the terms of
|
||||
that license document, provided that the further restriction does not survive such
|
||||
relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you must place, in
|
||||
the relevant source files, a statement of the additional terms that apply to those
|
||||
files, or a notice indicating where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the form of a
|
||||
separately written license, or stated as exceptions; the above requirements apply
|
||||
either way.
|
||||
|
||||
### 8. Termination
|
||||
|
||||
You may not propagate or modify a covered work except as expressly provided under
|
||||
this License. Any attempt otherwise to propagate or modify it is void, and will
|
||||
automatically terminate your rights under this License (including any patent licenses
|
||||
granted under the third paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your license from a
|
||||
particular copyright holder is reinstated **(a)** provisionally, unless and until the
|
||||
copyright holder explicitly and finally terminates your license, and **(b)** permanently,
|
||||
if the copyright holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is reinstated permanently
|
||||
if the copyright holder notifies you of the violation by some reasonable means, this
|
||||
is the first time you have received notice of violation of this License (for any
|
||||
work) from that copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the licenses of
|
||||
parties who have received copies or rights from you under this License. If your
|
||||
rights have been terminated and not permanently reinstated, you do not qualify to
|
||||
receive new licenses for the same material under section 10.
|
||||
|
||||
### 9. Acceptance Not Required for Having Copies
|
||||
|
||||
You are not required to accept this License in order to receive or run a copy of the
|
||||
Program. Ancillary propagation of a covered work occurring solely as a consequence of
|
||||
using peer-to-peer transmission to receive a copy likewise does not require
|
||||
acceptance. However, nothing other than this License grants you permission to
|
||||
propagate or modify any covered work. These actions infringe copyright if you do not
|
||||
accept this License. Therefore, by modifying or propagating a covered work, you
|
||||
indicate your acceptance of this License to do so.
|
||||
|
||||
### 10. Automatic Licensing of Downstream Recipients
|
||||
|
||||
Each time you convey a covered work, the recipient automatically receives a license
|
||||
from the original licensors, to run, modify and propagate that work, subject to this
|
||||
License. You are not responsible for enforcing compliance by third parties with this
|
||||
License.
|
||||
|
||||
An “entity transaction” is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an organization, or
|
||||
merging organizations. If propagation of a covered work results from an entity
|
||||
transaction, each party to that transaction who receives a copy of the work also
|
||||
receives whatever licenses to the work the party's predecessor in interest had or
|
||||
could give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if the predecessor
|
||||
has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the rights granted or
|
||||
affirmed under this License. For example, you may not impose a license fee, royalty,
|
||||
or other charge for exercise of rights granted under this License, and you may not
|
||||
initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that any patent claim is infringed by making, using, selling, offering for sale, or
|
||||
importing the Program or any portion of it.
|
||||
|
||||
### 11. Patents
|
||||
|
||||
A “contributor” is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The work thus
|
||||
licensed is called the contributor's “contributor version”.
|
||||
|
||||
A contributor's “essential patent claims” are all patent claims owned or
|
||||
controlled by the contributor, whether already acquired or hereafter acquired, that
|
||||
would be infringed by some manner, permitted by this License, of making, using, or
|
||||
selling its contributor version, but do not include claims that would be infringed
|
||||
only as a consequence of further modification of the contributor version. For
|
||||
purposes of this definition, “control” includes the right to grant patent
|
||||
sublicenses in a manner consistent with the requirements of this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free patent license
|
||||
under the contributor's essential patent claims, to make, use, sell, offer for sale,
|
||||
import and otherwise run, modify and propagate the contents of its contributor
|
||||
version.
|
||||
|
||||
In the following three paragraphs, a “patent license” is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent (such as an
|
||||
express permission to practice a patent or covenant not to sue for patent
|
||||
infringement). To “grant” such a patent license to a party means to make
|
||||
such an agreement or commitment not to enforce a patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license, and the
|
||||
Corresponding Source of the work is not available for anyone to copy, free of charge
|
||||
and under the terms of this License, through a publicly available network server or
|
||||
other readily accessible means, then you must either **(1)** cause the Corresponding
|
||||
Source to be so available, or **(2)** arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or **(3)** arrange, in a manner consistent with
|
||||
the requirements of this License, to extend the patent license to downstream
|
||||
recipients. “Knowingly relying” means you have actual knowledge that, but
|
||||
for the patent license, your conveying the covered work in a country, or your
|
||||
recipient's use of the covered work in a country, would infringe one or more
|
||||
identifiable patents in that country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or arrangement, you
|
||||
convey, or propagate by procuring conveyance of, a covered work, and grant a patent
|
||||
license to some of the parties receiving the covered work authorizing them to use,
|
||||
propagate, modify or convey a specific copy of the covered work, then the patent
|
||||
license you grant is automatically extended to all recipients of the covered work and
|
||||
works based on it.
|
||||
|
||||
A patent license is “discriminatory” if it does not include within the
|
||||
scope of its coverage, prohibits the exercise of, or is conditioned on the
|
||||
non-exercise of one or more of the rights that are specifically granted under this
|
||||
License. You may not convey a covered work if you are a party to an arrangement with
|
||||
a third party that is in the business of distributing software, under which you make
|
||||
payment to the third party based on the extent of your activity of conveying the
|
||||
work, and under which the third party grants, to any of the parties who would receive
|
||||
the covered work from you, a discriminatory patent license **(a)** in connection with
|
||||
copies of the covered work conveyed by you (or copies made from those copies), or **(b)**
|
||||
primarily for and in connection with specific products or compilations that contain
|
||||
the covered work, unless you entered into that arrangement, or that patent license
|
||||
was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting any implied
|
||||
license or other defenses to infringement that may otherwise be available to you
|
||||
under applicable patent law.
|
||||
|
||||
### 12. No Surrender of Others' Freedom
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or otherwise)
|
||||
that contradict the conditions of this License, they do not excuse you from the
|
||||
conditions of this License. If you cannot convey a covered work so as to satisfy
|
||||
simultaneously your obligations under this License and any other pertinent
|
||||
obligations, then as a consequence you may not convey it at all. For example, if you
|
||||
agree to terms that obligate you to collect a royalty for further conveying from
|
||||
those to whom you convey the Program, the only way you could satisfy both those terms
|
||||
and this License would be to refrain entirely from conveying the Program.
|
||||
|
||||
### 13. Use with the GNU Affero General Public License
|
||||
|
||||
Notwithstanding any other provision of this License, you have permission to link or
|
||||
combine any covered work with a work licensed under version 3 of the GNU Affero
|
||||
General Public License into a single combined work, and to convey the resulting work.
|
||||
The terms of this License will continue to apply to the part which is the covered
|
||||
work, but the special requirements of the GNU Affero General Public License, section
|
||||
13, concerning interaction through a network will apply to the combination as such.
|
||||
|
||||
### 14. Revised Versions of this License
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of the GNU
|
||||
General Public License from time to time. Such new versions will be similar in spirit
|
||||
to the present version, but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program specifies that
|
||||
a certain numbered version of the GNU General Public License “or any later
|
||||
version” applies to it, you have the option of following the terms and
|
||||
conditions either of that numbered version or of any later version published by the
|
||||
Free Software Foundation. If the Program does not specify a version number of the GNU
|
||||
General Public License, you may choose any version ever published by the Free
|
||||
Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future versions of the GNU
|
||||
General Public License can be used, that proxy's public statement of acceptance of a
|
||||
version permanently authorizes you to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different permissions. However, no
|
||||
additional obligations are imposed on any author or copyright holder as a result of
|
||||
your choosing to follow a later version.
|
||||
|
||||
### 15. Disclaimer of Warranty
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE
|
||||
QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE
|
||||
DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
### 16. Limitation of Liability
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY
|
||||
COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS
|
||||
PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL,
|
||||
INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE
|
||||
OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
|
||||
WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
### 17. Interpretation of Sections 15 and 16
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided above cannot be
|
||||
given local legal effect according to their terms, reviewing courts shall apply local
|
||||
law that most closely approximates an absolute waiver of all civil liability in
|
||||
connection with the Program, unless a warranty or assumption of liability accompanies
|
||||
a copy of the Program in return for a fee.
|
||||
|
||||
_END OF TERMS AND CONDITIONS_
|
||||
|
||||
## How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest possible use to
|
||||
the public, the best way to achieve this is to make it free software which everyone
|
||||
can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest to attach them
|
||||
to the start of each source file to most effectively state the exclusion of warranty;
|
||||
and each file should have at least the “copyright” line and a pointer to
|
||||
where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type 'show c' for details.
|
||||
|
||||
The hypothetical commands `show w` and `show c` should show the appropriate parts of
|
||||
the General Public License. Of course, your program's commands might be different;
|
||||
for a GUI interface, you would use an “about box”.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school, if any, to
|
||||
sign a “copyright disclaimer” for the program, if necessary. For more
|
||||
information on this, and how to apply and follow the GNU GPL, see
|
||||
<<http://www.gnu.org/licenses/>>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may consider it
|
||||
more useful to permit linking proprietary applications with the library. If this is
|
||||
what you want to do, use the GNU Lesser General Public License instead of this
|
||||
License. But first, please read
|
||||
<<http://www.gnu.org/philosophy/why-not-lgpl.html>>.
|
5
asg/README.md
Normal file
5
asg/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# leo-asg
|
||||
|
||||
[![Crates.io](https://img.shields.io/crates/v/leo-asg.svg?color=neon)](https://crates.io/crates/leo-asg)
|
||||
[![Authors](https://img.shields.io/badge/authors-Aleo-orange.svg)](../AUTHORS)
|
||||
[![License](https://img.shields.io/badge/License-GPLv3-blue.svg)](./LICENSE.md)
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2020 Aleo Systems Inc.
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
@ -14,9 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#[macro_use]
|
||||
pub mod eq;
|
||||
pub use self::eq::*;
|
||||
//! Helper methods to determine the correct return value path in an asg.
|
||||
|
||||
pub mod cmp;
|
||||
pub use self::cmp::*;
|
||||
mod return_path;
|
||||
pub use return_path::*;
|
125
asg/src/checks/return_path.rs
Normal file
125
asg/src/checks/return_path.rs
Normal file
@ -0,0 +1,125 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
statement::*,
|
||||
BoolAnd,
|
||||
Expression,
|
||||
Monoid,
|
||||
MonoidalReducerExpression,
|
||||
MonoidalReducerStatement,
|
||||
Node,
|
||||
Span,
|
||||
};
|
||||
|
||||
pub struct ReturnPathReducer {
|
||||
pub errors: Vec<(Span, String)>,
|
||||
}
|
||||
|
||||
impl ReturnPathReducer {
|
||||
fn record_error(&mut self, span: Option<&Span>, error: String) {
|
||||
self.errors.push((span.cloned().unwrap_or_default(), error));
|
||||
}
|
||||
|
||||
pub fn new() -> ReturnPathReducer {
|
||||
ReturnPathReducer { errors: vec![] }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ReturnPathReducer {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
impl<'a> MonoidalReducerExpression<'a, BoolAnd> for ReturnPathReducer {
|
||||
fn reduce_expression(&mut self, input: &'a Expression<'a>, value: BoolAnd) -> BoolAnd {
|
||||
BoolAnd(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
impl<'a> MonoidalReducerStatement<'a, BoolAnd> for ReturnPathReducer {
|
||||
fn reduce_assign_access(&mut self, input: &AssignAccess, left: Option<BoolAnd>, right: Option<BoolAnd>) -> BoolAnd {
|
||||
BoolAnd(false)
|
||||
}
|
||||
|
||||
fn reduce_assign(&mut self, input: &AssignStatement, accesses: Vec<BoolAnd>, value: BoolAnd) -> BoolAnd {
|
||||
BoolAnd(false)
|
||||
}
|
||||
|
||||
fn reduce_block(&mut self, input: &BlockStatement, statements: Vec<BoolAnd>) -> BoolAnd {
|
||||
if statements.is_empty() {
|
||||
BoolAnd(false)
|
||||
} else if let Some(index) = statements[..statements.len() - 1].iter().map(|x| x.0).position(|x| x) {
|
||||
self.record_error(
|
||||
input.statements[index].get().span(),
|
||||
"dead code due to unconditional early return".to_string(),
|
||||
);
|
||||
BoolAnd(true)
|
||||
} else {
|
||||
BoolAnd(statements[statements.len() - 1].0)
|
||||
}
|
||||
}
|
||||
|
||||
fn reduce_conditional_statement(
|
||||
&mut self,
|
||||
input: &ConditionalStatement,
|
||||
condition: BoolAnd,
|
||||
if_true: BoolAnd,
|
||||
if_false: Option<BoolAnd>,
|
||||
) -> BoolAnd {
|
||||
if if_false.as_ref().map(|x| x.0).unwrap_or(false) != if_true.0 {
|
||||
self.record_error(
|
||||
input.span(),
|
||||
"cannot have asymmetrical return in if statement".to_string(),
|
||||
);
|
||||
}
|
||||
if_true.append(if_false.unwrap_or(BoolAnd(false)))
|
||||
}
|
||||
|
||||
fn reduce_formatted_string(&mut self, input: &FormatString, parameters: Vec<BoolAnd>) -> BoolAnd {
|
||||
BoolAnd(false)
|
||||
}
|
||||
|
||||
fn reduce_console(&mut self, input: &ConsoleStatement, argument: BoolAnd) -> BoolAnd {
|
||||
BoolAnd(false)
|
||||
}
|
||||
|
||||
fn reduce_definition(&mut self, input: &DefinitionStatement, value: BoolAnd) -> BoolAnd {
|
||||
BoolAnd(false)
|
||||
}
|
||||
|
||||
fn reduce_expression_statement(&mut self, input: &ExpressionStatement, expression: BoolAnd) -> BoolAnd {
|
||||
BoolAnd(false)
|
||||
}
|
||||
|
||||
fn reduce_iteration(
|
||||
&mut self,
|
||||
input: &IterationStatement,
|
||||
start: BoolAnd,
|
||||
stop: BoolAnd,
|
||||
body: BoolAnd,
|
||||
) -> BoolAnd {
|
||||
// loops are const defined ranges, so we could probably check if they run one and emit here
|
||||
BoolAnd(false)
|
||||
}
|
||||
|
||||
fn reduce_return(&mut self, input: &ReturnStatement, value: BoolAnd) -> BoolAnd {
|
||||
BoolAnd(true)
|
||||
}
|
||||
}
|
325
asg/src/const_value.rs
Normal file
325
asg/src/const_value.rs
Normal file
@ -0,0 +1,325 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{AsgConvertError, IntegerType, Span, Type};
|
||||
|
||||
use num_bigint::BigInt;
|
||||
use std::{convert::TryInto, fmt};
|
||||
use tendril::StrTendril;
|
||||
|
||||
/// Constant integer values in a program.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ConstInt {
|
||||
I8(i8),
|
||||
I16(i16),
|
||||
I32(i32),
|
||||
I64(i64),
|
||||
I128(i128),
|
||||
U8(u8),
|
||||
U16(u16),
|
||||
U32(u32),
|
||||
U64(u64),
|
||||
U128(u128),
|
||||
}
|
||||
|
||||
/// Specifies how to calculate a group coordinate in a program.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum GroupCoordinate {
|
||||
/// Explicit field element number string.
|
||||
Number(StrTendril),
|
||||
|
||||
/// Attempt to recover with a sign high bit.
|
||||
SignHigh,
|
||||
|
||||
/// Attempt to recover with a sign low bit.
|
||||
SignLow,
|
||||
|
||||
/// Try recovering with a sign low - upon failure try sign high.
|
||||
Inferred,
|
||||
}
|
||||
|
||||
impl fmt::Display for GroupCoordinate {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
GroupCoordinate::Number(number) => write!(f, "{}", number),
|
||||
GroupCoordinate::SignHigh => write!(f, "+"),
|
||||
GroupCoordinate::SignLow => write!(f, "-"),
|
||||
GroupCoordinate::Inferred => write!(f, "_"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&leo_ast::GroupCoordinate> for GroupCoordinate {
|
||||
fn from(other: &leo_ast::GroupCoordinate) -> GroupCoordinate {
|
||||
use leo_ast::GroupCoordinate::*;
|
||||
match other {
|
||||
Number(value, _) => GroupCoordinate::Number(value.clone()),
|
||||
SignHigh => GroupCoordinate::SignHigh,
|
||||
SignLow => GroupCoordinate::SignLow,
|
||||
Inferred => GroupCoordinate::Inferred,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<leo_ast::GroupCoordinate> for &GroupCoordinate {
|
||||
fn into(self) -> leo_ast::GroupCoordinate {
|
||||
use GroupCoordinate::*;
|
||||
match self {
|
||||
Number(value) => leo_ast::GroupCoordinate::Number(value.clone(), Default::default()),
|
||||
SignHigh => leo_ast::GroupCoordinate::SignHigh,
|
||||
SignLow => leo_ast::GroupCoordinate::SignLow,
|
||||
Inferred => leo_ast::GroupCoordinate::Inferred,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum GroupValue {
|
||||
Single(StrTendril),
|
||||
Tuple(GroupCoordinate, GroupCoordinate),
|
||||
}
|
||||
|
||||
impl From<leo_ast::GroupValue> for GroupValue {
|
||||
fn from(other: leo_ast::GroupValue) -> Self {
|
||||
use leo_ast::GroupValue::*;
|
||||
match other {
|
||||
Single(value, _) => GroupValue::Single(value),
|
||||
Tuple(value) => GroupValue::Tuple(GroupCoordinate::from(&value.x), GroupCoordinate::from(&value.y)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ConstValue {
|
||||
Int(ConstInt),
|
||||
Group(GroupValue),
|
||||
Field(BigInt),
|
||||
Address(StrTendril),
|
||||
Boolean(bool),
|
||||
|
||||
// compounds
|
||||
Tuple(Vec<ConstValue>),
|
||||
Array(Vec<ConstValue>),
|
||||
}
|
||||
|
||||
macro_rules! const_int_op {
|
||||
($name: ident, $retType: ty, $x: ident, $transform: expr) => {
|
||||
pub fn $name(&self) -> $retType {
|
||||
match self {
|
||||
ConstInt::I8($x) => $transform,
|
||||
ConstInt::I16($x) => $transform,
|
||||
ConstInt::I32($x) => $transform,
|
||||
ConstInt::I64($x) => $transform,
|
||||
ConstInt::I128($x) => $transform,
|
||||
ConstInt::U8($x) => $transform,
|
||||
ConstInt::U16($x) => $transform,
|
||||
ConstInt::U32($x) => $transform,
|
||||
ConstInt::U64($x) => $transform,
|
||||
ConstInt::U128($x) => $transform,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! const_int_biop {
|
||||
($name: ident, $retType: ty, $x: ident, $y: ident, $transform: expr) => {
|
||||
pub fn $name(&self, other: &ConstInt) -> Option<$retType> {
|
||||
match (self, other) {
|
||||
(ConstInt::I8($x), ConstInt::I8($y)) => $transform,
|
||||
(ConstInt::I16($x), ConstInt::I16($y)) => $transform,
|
||||
(ConstInt::I32($x), ConstInt::I32($y)) => $transform,
|
||||
(ConstInt::I64($x), ConstInt::I64($y)) => $transform,
|
||||
(ConstInt::I128($x), ConstInt::I128($y)) => $transform,
|
||||
(ConstInt::U8($x), ConstInt::U8($y)) => $transform,
|
||||
(ConstInt::U16($x), ConstInt::U16($y)) => $transform,
|
||||
(ConstInt::U32($x), ConstInt::U32($y)) => $transform,
|
||||
(ConstInt::U64($x), ConstInt::U64($y)) => $transform,
|
||||
(ConstInt::U128($x), ConstInt::U128($y)) => $transform,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! const_int_map {
|
||||
($name: ident, $x: ident, $transform: expr) => {
|
||||
pub fn $name(&self) -> Option<ConstInt> {
|
||||
Some(match self {
|
||||
ConstInt::I8($x) => ConstInt::I8($transform),
|
||||
ConstInt::I16($x) => ConstInt::I16($transform),
|
||||
ConstInt::I32($x) => ConstInt::I32($transform),
|
||||
ConstInt::I64($x) => ConstInt::I64($transform),
|
||||
ConstInt::I128($x) => ConstInt::I128($transform),
|
||||
ConstInt::U8($x) => ConstInt::U8($transform),
|
||||
ConstInt::U16($x) => ConstInt::U16($transform),
|
||||
ConstInt::U32($x) => ConstInt::U32($transform),
|
||||
ConstInt::U64($x) => ConstInt::U64($transform),
|
||||
ConstInt::U128($x) => ConstInt::U128($transform),
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! const_int_bimap {
|
||||
($name: ident, $x: ident, $y: ident, $transform: expr) => {
|
||||
pub fn $name(&self, other: &ConstInt) -> Option<ConstInt> {
|
||||
Some(match (self, other) {
|
||||
(ConstInt::I8($x), ConstInt::I8($y)) => ConstInt::I8($transform),
|
||||
(ConstInt::I16($x), ConstInt::I16($y)) => ConstInt::I16($transform),
|
||||
(ConstInt::I32($x), ConstInt::I32($y)) => ConstInt::I32($transform),
|
||||
(ConstInt::I64($x), ConstInt::I64($y)) => ConstInt::I64($transform),
|
||||
(ConstInt::I128($x), ConstInt::I128($y)) => ConstInt::I128($transform),
|
||||
(ConstInt::U8($x), ConstInt::U8($y)) => ConstInt::U8($transform),
|
||||
(ConstInt::U16($x), ConstInt::U16($y)) => ConstInt::U16($transform),
|
||||
(ConstInt::U32($x), ConstInt::U32($y)) => ConstInt::U32($transform),
|
||||
(ConstInt::U64($x), ConstInt::U64($y)) => ConstInt::U64($transform),
|
||||
(ConstInt::U128($x), ConstInt::U128($y)) => ConstInt::U128($transform),
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(clippy::useless_conversion)]
|
||||
impl ConstInt {
|
||||
const_int_op!(raw_value, String, x, format!("{}", x));
|
||||
|
||||
const_int_map!(value_negate, x, x.checked_neg()?);
|
||||
|
||||
const_int_map!(value_bit_negate, x, !x);
|
||||
|
||||
const_int_op!(to_usize, Option<usize>, x, (*x).try_into().ok());
|
||||
|
||||
const_int_op!(to_u128, u128, x, *x as u128);
|
||||
|
||||
const_int_op!(to_u64, u64, x, *x as u64);
|
||||
|
||||
const_int_op!(to_u32, u32, x, *x as u32);
|
||||
|
||||
const_int_op!(to_u16, u16, x, *x as u16);
|
||||
|
||||
const_int_op!(to_u8, u8, x, *x as u8);
|
||||
|
||||
const_int_op!(to_i128, i128, x, *x as i128);
|
||||
|
||||
const_int_op!(to_i64, i64, x, *x as i64);
|
||||
|
||||
const_int_op!(to_i32, i32, x, *x as i32);
|
||||
|
||||
const_int_op!(to_i16, i16, x, *x as i16);
|
||||
|
||||
const_int_op!(to_i8, i8, x, *x as i8);
|
||||
|
||||
const_int_op!(to_string, String, x, (*x).to_string());
|
||||
|
||||
const_int_bimap!(value_add, x, y, x.checked_add(*y)?);
|
||||
|
||||
const_int_bimap!(value_sub, x, y, x.checked_sub(*y)?);
|
||||
|
||||
const_int_bimap!(value_mul, x, y, x.checked_mul(*y)?);
|
||||
|
||||
const_int_bimap!(value_div, x, y, x.checked_div(*y)?);
|
||||
|
||||
// TODO: limited to 32 bit exponents
|
||||
const_int_bimap!(value_pow, x, y, x.checked_pow((*y).try_into().ok()?)?);
|
||||
|
||||
const_int_biop!(value_lt, bool, x, y, Some(x < y));
|
||||
|
||||
const_int_biop!(value_le, bool, x, y, Some(x <= y));
|
||||
|
||||
const_int_biop!(value_gt, bool, x, y, Some(x > y));
|
||||
|
||||
const_int_biop!(value_ge, bool, x, y, Some(x >= y));
|
||||
|
||||
pub fn get_int_type(&self) -> IntegerType {
|
||||
match self {
|
||||
ConstInt::I8(_) => IntegerType::I8,
|
||||
ConstInt::I16(_) => IntegerType::I16,
|
||||
ConstInt::I32(_) => IntegerType::I32,
|
||||
ConstInt::I64(_) => IntegerType::I64,
|
||||
ConstInt::I128(_) => IntegerType::I128,
|
||||
ConstInt::U8(_) => IntegerType::U8,
|
||||
ConstInt::U16(_) => IntegerType::U16,
|
||||
ConstInt::U32(_) => IntegerType::U32,
|
||||
ConstInt::U64(_) => IntegerType::U64,
|
||||
ConstInt::U128(_) => IntegerType::U128,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cast_to(&self, target: &IntegerType) -> ConstInt {
|
||||
match target {
|
||||
IntegerType::I8 => ConstInt::I8(self.to_i8()),
|
||||
IntegerType::I16 => ConstInt::I16(self.to_i16()),
|
||||
IntegerType::I32 => ConstInt::I32(self.to_i32()),
|
||||
IntegerType::I64 => ConstInt::I64(self.to_i64()),
|
||||
IntegerType::I128 => ConstInt::I128(self.to_i128()),
|
||||
IntegerType::U8 => ConstInt::U8(self.to_u8()),
|
||||
IntegerType::U16 => ConstInt::U16(self.to_u16()),
|
||||
IntegerType::U32 => ConstInt::U32(self.to_u32()),
|
||||
IntegerType::U64 => ConstInt::U64(self.to_u64()),
|
||||
IntegerType::U128 => ConstInt::U128(self.to_u128()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_type<'a>(&self) -> Type<'a> {
|
||||
Type::Integer(self.get_int_type())
|
||||
}
|
||||
|
||||
pub fn parse(int_type: &IntegerType, value: &str, span: &Span) -> Result<ConstInt, AsgConvertError> {
|
||||
Ok(match int_type {
|
||||
IntegerType::I8 => ConstInt::I8(value.parse().map_err(|_| AsgConvertError::invalid_int(&value, span))?),
|
||||
IntegerType::I16 => ConstInt::I16(value.parse().map_err(|_| AsgConvertError::invalid_int(&value, span))?),
|
||||
IntegerType::I32 => ConstInt::I32(value.parse().map_err(|_| AsgConvertError::invalid_int(&value, span))?),
|
||||
IntegerType::I64 => ConstInt::I64(value.parse().map_err(|_| AsgConvertError::invalid_int(&value, span))?),
|
||||
IntegerType::I128 => ConstInt::I128(value.parse().map_err(|_| AsgConvertError::invalid_int(&value, span))?),
|
||||
IntegerType::U8 => ConstInt::U8(value.parse().map_err(|_| AsgConvertError::invalid_int(&value, span))?),
|
||||
IntegerType::U16 => ConstInt::U16(value.parse().map_err(|_| AsgConvertError::invalid_int(&value, span))?),
|
||||
IntegerType::U32 => ConstInt::U32(value.parse().map_err(|_| AsgConvertError::invalid_int(&value, span))?),
|
||||
IntegerType::U64 => ConstInt::U64(value.parse().map_err(|_| AsgConvertError::invalid_int(&value, span))?),
|
||||
IntegerType::U128 => ConstInt::U128(value.parse().map_err(|_| AsgConvertError::invalid_int(&value, span))?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ConstValue {
|
||||
pub fn get_type<'a>(&self) -> Option<Type<'a>> {
|
||||
Some(match self {
|
||||
ConstValue::Int(i) => i.get_type(),
|
||||
ConstValue::Group(_) => Type::Group,
|
||||
ConstValue::Field(_) => Type::Field,
|
||||
ConstValue::Address(_) => Type::Address,
|
||||
ConstValue::Boolean(_) => Type::Boolean,
|
||||
ConstValue::Tuple(sub_consts) => {
|
||||
Type::Tuple(sub_consts.iter().map(|x| x.get_type()).collect::<Option<Vec<Type>>>()?)
|
||||
}
|
||||
ConstValue::Array(values) => Type::Array(Box::new(values.get(0)?.get_type()?), values.len()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn int(&self) -> Option<&ConstInt> {
|
||||
match self {
|
||||
ConstValue::Int(x) => Some(x),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn field(&self) -> Option<&BigInt> {
|
||||
match self {
|
||||
ConstValue::Field(x) => Some(x),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
94
asg/src/context.rs
Normal file
94
asg/src/context.rs
Normal file
@ -0,0 +1,94 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{cell::Cell, unimplemented};
|
||||
|
||||
use typed_arena::Arena;
|
||||
|
||||
use crate::{ArenaNode, Circuit, Expression, Function, Scope, Statement, Variable};
|
||||
|
||||
pub struct AsgContextInner<'a> {
|
||||
pub arena: &'a Arena<ArenaNode<'a>>,
|
||||
pub next_id: Cell<u32>,
|
||||
}
|
||||
|
||||
impl<'a> AsgContextInner<'a> {
|
||||
pub fn new(arena: &'a Arena<ArenaNode<'a>>) -> &'a Self {
|
||||
match arena.alloc(ArenaNode::Inner(AsgContextInner {
|
||||
arena,
|
||||
next_id: Cell::new(0),
|
||||
})) {
|
||||
ArenaNode::Inner(x) => x,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_id(&self) -> u32 {
|
||||
let next_id = self.next_id.get();
|
||||
self.next_id.replace(next_id + 1);
|
||||
next_id
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub fn alloc_expression(&'a self, expr: Expression<'a>) -> &'a Expression<'a> {
|
||||
match self.arena.alloc(ArenaNode::Expression(expr)) {
|
||||
ArenaNode::Expression(e) => e,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub fn alloc_statement(&'a self, statement: Statement<'a>) -> &'a Statement<'a> {
|
||||
match self.arena.alloc(ArenaNode::Statement(statement)) {
|
||||
ArenaNode::Statement(e) => e,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub fn alloc_variable(&'a self, variable: Variable<'a>) -> &'a Variable<'a> {
|
||||
match self.arena.alloc(ArenaNode::Variable(variable)) {
|
||||
ArenaNode::Variable(e) => e,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub fn alloc_scope(&'a self, scope: Scope<'a>) -> &'a Scope<'a> {
|
||||
match self.arena.alloc(ArenaNode::Scope(Box::new(scope))) {
|
||||
ArenaNode::Scope(e) => e,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub fn alloc_circuit(&'a self, circuit: Circuit<'a>) -> &'a Circuit<'a> {
|
||||
match self.arena.alloc(ArenaNode::Circuit(circuit)) {
|
||||
ArenaNode::Circuit(e) => e,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub fn alloc_function(&'a self, function: Function<'a>) -> &'a Function<'a> {
|
||||
match self.arena.alloc(ArenaNode::Function(function)) {
|
||||
ArenaNode::Function(e) => e,
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type AsgContext<'a> = &'a AsgContextInner<'a>;
|
300
asg/src/error/mod.rs
Normal file
300
asg/src/error/mod.rs
Normal file
@ -0,0 +1,300 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Errors encountered when attempting to convert to an asg from an ast.
|
||||
|
||||
use crate::Span;
|
||||
use leo_ast::{FormattedError, LeoError};
|
||||
use leo_parser::SyntaxError;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum AsgConvertError {
|
||||
#[error("{}", _0)]
|
||||
Error(#[from] FormattedError),
|
||||
|
||||
#[error("{}", _0)]
|
||||
ImportError(FormattedError),
|
||||
|
||||
#[error("{}", _0)]
|
||||
InternalError(String),
|
||||
|
||||
#[error("{}", _0)]
|
||||
SyntaxError(#[from] SyntaxError),
|
||||
}
|
||||
|
||||
impl LeoError for AsgConvertError {}
|
||||
|
||||
impl AsgConvertError {
|
||||
fn new_from_span(message: String, span: &Span) -> Self {
|
||||
AsgConvertError::Error(FormattedError::new_from_span(message, span))
|
||||
}
|
||||
|
||||
pub fn unresolved_circuit(name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(format!("failed to resolve circuit: '{}'", name), span)
|
||||
}
|
||||
|
||||
pub fn unresolved_import(name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(format!("failed to resolve import: '{}'", name), span)
|
||||
}
|
||||
|
||||
pub fn unresolved_circuit_member(circuit_name: &str, name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(
|
||||
format!(
|
||||
"illegal reference to non-existant member '{}' of circuit '{}'",
|
||||
name, circuit_name
|
||||
),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn missing_circuit_member(circuit_name: &str, name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(
|
||||
format!(
|
||||
"missing circuit member '{}' for initialization of circuit '{}'",
|
||||
name, circuit_name
|
||||
),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn overridden_circuit_member(circuit_name: &str, name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(
|
||||
format!(
|
||||
"cannot declare circuit member '{}' more than once for initialization of circuit '{}'",
|
||||
name, circuit_name
|
||||
),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn redefined_circuit_member(circuit_name: &str, name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(
|
||||
format!(
|
||||
"cannot declare circuit member '{}' multiple times in circuit '{}'",
|
||||
name, circuit_name
|
||||
),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn extra_circuit_member(circuit_name: &str, name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(
|
||||
format!(
|
||||
"extra circuit member '{}' for initialization of circuit '{}' is not allowed",
|
||||
name, circuit_name
|
||||
),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn illegal_function_assign(name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(format!("attempt to assign to function '{}'", name), span)
|
||||
}
|
||||
|
||||
pub fn circuit_variable_call(circuit_name: &str, name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(
|
||||
format!("cannot call variable member '{}' of circuit '{}'", name, circuit_name),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn circuit_static_call_invalid(circuit_name: &str, name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(
|
||||
format!(
|
||||
"cannot call static function '{}' of circuit '{}' from target",
|
||||
name, circuit_name
|
||||
),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn circuit_member_mut_call_invalid(circuit_name: &str, name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(
|
||||
format!(
|
||||
"cannot call mutable member function '{}' of circuit '{}' from immutable context",
|
||||
name, circuit_name
|
||||
),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn circuit_member_call_invalid(circuit_name: &str, name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(
|
||||
format!(
|
||||
"cannot call member function '{}' of circuit '{}' from static context",
|
||||
name, circuit_name
|
||||
),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn circuit_function_ref(circuit_name: &str, name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(
|
||||
format!(
|
||||
"cannot reference function member '{}' of circuit '{}' as value",
|
||||
name, circuit_name
|
||||
),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn index_into_non_array(name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(format!("failed to index into non-array '{}'", name), span)
|
||||
}
|
||||
|
||||
pub fn invalid_assign_index(name: &str, num: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(
|
||||
format!("failed to index array with invalid integer '{}'[{}]", name, num),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn invalid_backwards_assignment(name: &str, left: usize, right: usize, span: &Span) -> Self {
|
||||
Self::new_from_span(
|
||||
format!(
|
||||
"failed to index array range for assignment with left > right '{}'[{}..{}]",
|
||||
name, left, right
|
||||
),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn duplicate_function_definition(name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(
|
||||
format!("a function named \"{}\" already exists in this scope", name),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn index_into_non_tuple(name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(format!("failed to index into non-tuple '{}'", name), span)
|
||||
}
|
||||
|
||||
pub fn tuple_index_out_of_bounds(index: usize, span: &Span) -> Self {
|
||||
Self::new_from_span(format!("tuple index out of bounds: '{}'", index), span)
|
||||
}
|
||||
|
||||
pub fn array_index_out_of_bounds(index: usize, span: &Span) -> Self {
|
||||
Self::new_from_span(format!("array index out of bounds: '{}'", index), span)
|
||||
}
|
||||
|
||||
pub fn unknown_array_size(span: &Span) -> Self {
|
||||
Self::new_from_span("array size cannot be inferred, add explicit types".to_string(), span)
|
||||
}
|
||||
|
||||
pub fn unexpected_call_argument_count(expected: usize, got: usize, span: &Span) -> Self {
|
||||
Self::new_from_span(
|
||||
format!("function call expected {} arguments, got {}", expected, got),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn unresolved_function(name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(format!("failed to resolve function: '{}'", name), span)
|
||||
}
|
||||
|
||||
pub fn unresolved_type(name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(
|
||||
format!("failed to resolve type for variable definition '{}'", name),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn unexpected_type(expected: &str, received: Option<&str>, span: &Span) -> Self {
|
||||
// panic!(format!("unexpected type, expected: '{}', received: '{}'", expected, received.unwrap_or("unknown")));
|
||||
Self::new_from_span(
|
||||
format!(
|
||||
"unexpected type, expected: '{}', received: '{}'",
|
||||
expected,
|
||||
received.unwrap_or("unknown")
|
||||
),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn unexpected_nonconst(span: &Span) -> Self {
|
||||
Self::new_from_span("expected const, found non-const value".to_string(), span)
|
||||
}
|
||||
|
||||
pub fn unresolved_reference(name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(format!("failed to resolve variable reference '{}'", name), span)
|
||||
}
|
||||
|
||||
pub fn invalid_boolean(value: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(format!("failed to parse boolean value '{}'", value), span)
|
||||
}
|
||||
|
||||
pub fn invalid_int(value: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(format!("failed to parse int value '{}'", value), span)
|
||||
}
|
||||
|
||||
pub fn unsigned_negation(span: &Span) -> Self {
|
||||
Self::new_from_span("cannot negate unsigned integer".to_string(), span)
|
||||
}
|
||||
|
||||
pub fn immutable_assignment(name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(format!("illegal assignment to immutable variable '{}'", name), span)
|
||||
}
|
||||
|
||||
pub fn function_missing_return(name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(format!("function '{}' missing return for all paths", name), span)
|
||||
}
|
||||
|
||||
pub fn function_return_validation(name: &str, description: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(
|
||||
format!("function '{}' failed to validate return path: '{}'", name, description),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn input_ref_needs_type(category: &str, name: &str, span: &Span) -> Self {
|
||||
Self::new_from_span(
|
||||
format!("could not infer type for input in '{}': '{}'", category, name),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn invalid_self_in_global(span: &Span) -> Self {
|
||||
Self::new_from_span(
|
||||
"cannot have `mut self` or `self` arguments in global functions".to_string(),
|
||||
span,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn call_test_function(span: &Span) -> Self {
|
||||
Self::new_from_span("cannot call test function".to_string(), span)
|
||||
}
|
||||
|
||||
pub fn circuit_test_function(span: &Span) -> Self {
|
||||
Self::new_from_span("cannot have test function as member of circuit".to_string(), span)
|
||||
}
|
||||
|
||||
pub fn parse_index_error() -> Self {
|
||||
AsgConvertError::InternalError("failed to parse index".to_string())
|
||||
}
|
||||
|
||||
pub fn parse_dimension_error() -> Self {
|
||||
AsgConvertError::InternalError("failed to parse dimension".to_string())
|
||||
}
|
||||
|
||||
pub fn reference_self_outside_circuit() -> Self {
|
||||
AsgConvertError::InternalError("referenced self outside of circuit function".to_string())
|
||||
}
|
||||
|
||||
pub fn illegal_ast_structure(details: &str) -> Self {
|
||||
AsgConvertError::InternalError(format!("illegal ast structure: {}", details))
|
||||
}
|
||||
}
|
139
asg/src/expression/array_access.rs
Normal file
139
asg/src/expression/array_access.rs
Normal file
@ -0,0 +1,139 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{AsgConvertError, ConstValue, Expression, ExpressionNode, FromAst, Node, PartialType, Scope, Span, Type};
|
||||
use leo_ast::IntegerType;
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ArrayAccessExpression<'a> {
|
||||
pub parent: Cell<Option<&'a Expression<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub array: Cell<&'a Expression<'a>>,
|
||||
pub index: Cell<&'a Expression<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Node for ArrayAccessExpression<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExpressionNode<'a> for ArrayAccessExpression<'a> {
|
||||
fn set_parent(&self, parent: &'a Expression<'a>) {
|
||||
self.parent.replace(Some(parent));
|
||||
}
|
||||
|
||||
fn get_parent(&self) -> Option<&'a Expression<'a>> {
|
||||
self.parent.get()
|
||||
}
|
||||
|
||||
fn enforce_parents(&self, expr: &'a Expression<'a>) {
|
||||
self.array.get().set_parent(expr);
|
||||
self.index.get().set_parent(expr);
|
||||
}
|
||||
|
||||
fn get_type(&self) -> Option<Type<'a>> {
|
||||
match self.array.get().get_type() {
|
||||
Some(Type::Array(element, _)) => Some(*element),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_mut_ref(&self) -> bool {
|
||||
self.array.get().is_mut_ref()
|
||||
}
|
||||
|
||||
fn const_value(&self) -> Option<ConstValue> {
|
||||
let mut array = match self.array.get().const_value()? {
|
||||
ConstValue::Array(values) => values,
|
||||
_ => return None,
|
||||
};
|
||||
let const_index = match self.index.get().const_value()? {
|
||||
ConstValue::Int(x) => x.to_usize()?,
|
||||
_ => return None,
|
||||
};
|
||||
if const_index >= array.len() {
|
||||
return None;
|
||||
}
|
||||
Some(array.remove(const_index))
|
||||
}
|
||||
|
||||
fn is_consty(&self) -> bool {
|
||||
self.array.get().is_consty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::ArrayAccessExpression> for ArrayAccessExpression<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &leo_ast::ArrayAccessExpression,
|
||||
expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<ArrayAccessExpression<'a>, AsgConvertError> {
|
||||
let array = <&Expression<'a>>::from_ast(
|
||||
scope,
|
||||
&*value.array,
|
||||
Some(PartialType::Array(expected_type.map(Box::new), None)),
|
||||
)?;
|
||||
let array_len = match array.get_type() {
|
||||
Some(Type::Array(_, len)) => len,
|
||||
type_ => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
"array",
|
||||
type_.map(|x| x.to_string()).as_deref(),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let index = <&Expression<'a>>::from_ast(
|
||||
scope,
|
||||
&*value.index,
|
||||
Some(PartialType::Integer(None, Some(IntegerType::U32))),
|
||||
)?;
|
||||
|
||||
if let Some(index) = index
|
||||
.const_value()
|
||||
.map(|x| x.int().map(|x| x.to_usize()).flatten())
|
||||
.flatten()
|
||||
{
|
||||
if index >= array_len {
|
||||
return Err(AsgConvertError::array_index_out_of_bounds(
|
||||
index,
|
||||
&array.span().cloned().unwrap_or_default(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ArrayAccessExpression {
|
||||
parent: Cell::new(None),
|
||||
span: Some(value.span.clone()),
|
||||
array: Cell::new(array),
|
||||
index: Cell::new(index),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::ArrayAccessExpression> for &ArrayAccessExpression<'a> {
|
||||
fn into(self) -> leo_ast::ArrayAccessExpression {
|
||||
leo_ast::ArrayAccessExpression {
|
||||
array: Box::new(self.array.get().into()),
|
||||
index: Box::new(self.index.get().into()),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
160
asg/src/expression/array_init.rs
Normal file
160
asg/src/expression/array_init.rs
Normal file
@ -0,0 +1,160 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{AsgConvertError, ConstValue, Expression, ExpressionNode, FromAst, Node, PartialType, Scope, Span, Type};
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ArrayInitExpression<'a> {
|
||||
pub parent: Cell<Option<&'a Expression<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub element: Cell<&'a Expression<'a>>,
|
||||
pub len: usize,
|
||||
}
|
||||
|
||||
impl<'a> Node for ArrayInitExpression<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExpressionNode<'a> for ArrayInitExpression<'a> {
|
||||
fn set_parent(&self, parent: &'a Expression<'a>) {
|
||||
self.parent.replace(Some(parent));
|
||||
}
|
||||
|
||||
fn get_parent(&self) -> Option<&'a Expression<'a>> {
|
||||
self.parent.get()
|
||||
}
|
||||
|
||||
fn enforce_parents(&self, expr: &'a Expression<'a>) {
|
||||
self.element.get().set_parent(expr);
|
||||
}
|
||||
|
||||
fn get_type(&self) -> Option<Type<'a>> {
|
||||
Some(Type::Array(Box::new(self.element.get().get_type()?), self.len))
|
||||
}
|
||||
|
||||
fn is_mut_ref(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn const_value(&self) -> Option<ConstValue> {
|
||||
// not implemented due to performance concerns
|
||||
None
|
||||
}
|
||||
|
||||
fn is_consty(&self) -> bool {
|
||||
self.element.get().is_consty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::ArrayInitExpression> for ArrayInitExpression<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &leo_ast::ArrayInitExpression,
|
||||
expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<ArrayInitExpression<'a>, AsgConvertError> {
|
||||
let (mut expected_item, expected_len) = match expected_type {
|
||||
Some(PartialType::Array(item, dims)) => (item.map(|x| *x), dims),
|
||||
None => (None, None),
|
||||
Some(type_) => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&type_.to_string(),
|
||||
Some("array"),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
};
|
||||
let dimensions = value
|
||||
.dimensions
|
||||
.0
|
||||
.iter()
|
||||
.map(|x| {
|
||||
x.value
|
||||
.parse::<usize>()
|
||||
.map_err(|_| AsgConvertError::parse_dimension_error())
|
||||
})
|
||||
.collect::<Result<Vec<_>, AsgConvertError>>()?;
|
||||
|
||||
let len = *dimensions.get(0).ok_or_else(AsgConvertError::parse_dimension_error)?;
|
||||
if let Some(expected_len) = expected_len {
|
||||
if expected_len != len {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&*format!("array of length {}", expected_len),
|
||||
Some(&*format!("array of length {}", len)),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
for dimension in (&dimensions[1..]).iter().copied() {
|
||||
expected_item = match expected_item {
|
||||
Some(PartialType::Array(item, len)) => {
|
||||
if let Some(len) = len {
|
||||
if len != dimension {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&*format!("array of length {}", dimension),
|
||||
Some(&*format!("array of length {}", len)),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
item.map(|x| *x)
|
||||
}
|
||||
None => None,
|
||||
Some(type_) => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
"array",
|
||||
Some(&type_.to_string()),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut element = Some(<&'a Expression<'a>>::from_ast(scope, &*value.element, expected_item)?);
|
||||
let mut output = None;
|
||||
|
||||
for dimension in dimensions.iter().rev().copied() {
|
||||
output = Some(ArrayInitExpression {
|
||||
parent: Cell::new(None),
|
||||
span: Some(value.span.clone()),
|
||||
element: Cell::new(
|
||||
output
|
||||
.map(Expression::ArrayInit)
|
||||
.map(|expr| &*scope.context.alloc_expression(expr))
|
||||
.unwrap_or_else(|| element.take().unwrap()),
|
||||
),
|
||||
len: dimension,
|
||||
});
|
||||
}
|
||||
Ok(output.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::ArrayInitExpression> for &ArrayInitExpression<'a> {
|
||||
fn into(self) -> leo_ast::ArrayInitExpression {
|
||||
leo_ast::ArrayInitExpression {
|
||||
element: Box::new(self.element.get().into()),
|
||||
dimensions: leo_ast::ArrayDimensions(vec![leo_ast::PositiveNumber {
|
||||
value: self.len.to_string().into(),
|
||||
}]),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
217
asg/src/expression/array_inline.rs
Normal file
217
asg/src/expression/array_inline.rs
Normal file
@ -0,0 +1,217 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{AsgConvertError, ConstValue, Expression, ExpressionNode, FromAst, Node, PartialType, Scope, Span, Type};
|
||||
use leo_ast::SpreadOrExpression;
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ArrayInlineExpression<'a> {
|
||||
pub parent: Cell<Option<&'a Expression<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub elements: Vec<(Cell<&'a Expression<'a>>, bool)>, // bool = if spread
|
||||
}
|
||||
|
||||
impl<'a> ArrayInlineExpression<'a> {
|
||||
pub fn expanded_length(&self) -> usize {
|
||||
self.elements
|
||||
.iter()
|
||||
.map(|(expr, is_spread)| {
|
||||
if *is_spread {
|
||||
match expr.get().get_type() {
|
||||
Some(Type::Array(_item, len)) => len,
|
||||
_ => 0,
|
||||
}
|
||||
} else {
|
||||
1
|
||||
}
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Node for ArrayInlineExpression<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExpressionNode<'a> for ArrayInlineExpression<'a> {
|
||||
fn set_parent(&self, parent: &'a Expression<'a>) {
|
||||
self.parent.replace(Some(parent));
|
||||
}
|
||||
|
||||
fn get_parent(&self) -> Option<&'a Expression<'a>> {
|
||||
self.parent.get()
|
||||
}
|
||||
|
||||
fn enforce_parents(&self, expr: &'a Expression<'a>) {
|
||||
self.elements.iter().for_each(|(element, _)| {
|
||||
element.get().set_parent(expr);
|
||||
})
|
||||
}
|
||||
|
||||
fn get_type(&self) -> Option<Type<'a>> {
|
||||
Some(Type::Array(
|
||||
Box::new(self.elements.first()?.0.get().get_type()?),
|
||||
self.expanded_length(),
|
||||
))
|
||||
}
|
||||
|
||||
fn is_mut_ref(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn const_value(&self) -> Option<ConstValue> {
|
||||
let mut const_values = vec![];
|
||||
for (expr, spread) in self.elements.iter() {
|
||||
if *spread {
|
||||
match expr.get().const_value()? {
|
||||
ConstValue::Array(items) => const_values.extend(items),
|
||||
_ => return None,
|
||||
}
|
||||
} else {
|
||||
const_values.push(expr.get().const_value()?);
|
||||
}
|
||||
}
|
||||
Some(ConstValue::Array(const_values))
|
||||
}
|
||||
|
||||
fn is_consty(&self) -> bool {
|
||||
self.elements.iter().all(|x| x.0.get().is_consty())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::ArrayInlineExpression> for ArrayInlineExpression<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &leo_ast::ArrayInlineExpression,
|
||||
expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<ArrayInlineExpression<'a>, AsgConvertError> {
|
||||
let (mut expected_item, expected_len) = match expected_type {
|
||||
Some(PartialType::Array(item, dims)) => (item.map(|x| *x), dims),
|
||||
None => (None, None),
|
||||
Some(type_) => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&type_.to_string(),
|
||||
Some("array"),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// If we still don't know the type iterate through processing to get a type.
|
||||
// Once we encouter the type break the loop so we process as little as possible.
|
||||
if expected_item.is_none() {
|
||||
for expr in value.elements.iter() {
|
||||
expected_item = match expr {
|
||||
SpreadOrExpression::Expression(e) => {
|
||||
match <&Expression<'a>>::from_ast(scope, e, expected_item.clone()) {
|
||||
Ok(expr) => expr.get_type().map(Type::partial),
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if expected_item.is_some() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut len = 0;
|
||||
|
||||
let output = ArrayInlineExpression {
|
||||
parent: Cell::new(None),
|
||||
span: Some(value.span.clone()),
|
||||
elements: value
|
||||
.elements
|
||||
.iter()
|
||||
.map(|e| match e {
|
||||
SpreadOrExpression::Expression(e) => {
|
||||
let expr = <&Expression<'a>>::from_ast(scope, e, expected_item.clone())?;
|
||||
if expected_item.is_none() {
|
||||
expected_item = expr.get_type().map(Type::partial);
|
||||
}
|
||||
len += 1;
|
||||
Ok((Cell::new(expr), false))
|
||||
}
|
||||
SpreadOrExpression::Spread(e) => {
|
||||
let expr = <&Expression<'a>>::from_ast(
|
||||
scope,
|
||||
e,
|
||||
Some(PartialType::Array(expected_item.clone().map(Box::new), None)),
|
||||
)?;
|
||||
|
||||
match expr.get_type() {
|
||||
Some(Type::Array(item, spread_len)) => {
|
||||
if expected_item.is_none() {
|
||||
expected_item = Some((*item).partial());
|
||||
}
|
||||
|
||||
len += spread_len;
|
||||
}
|
||||
type_ => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
expected_item
|
||||
.as_ref()
|
||||
.map(|x| x.to_string())
|
||||
.as_deref()
|
||||
.unwrap_or("unknown"),
|
||||
type_.map(|x| x.to_string()).as_deref(),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok((Cell::new(expr), true))
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<_>, AsgConvertError>>()?,
|
||||
};
|
||||
if let Some(expected_len) = expected_len {
|
||||
if len != expected_len {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&*format!("array of length {}", expected_len),
|
||||
Some(&*format!("array of length {}", len)),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(output)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::ArrayInlineExpression> for &ArrayInlineExpression<'a> {
|
||||
fn into(self) -> leo_ast::ArrayInlineExpression {
|
||||
leo_ast::ArrayInlineExpression {
|
||||
elements: self
|
||||
.elements
|
||||
.iter()
|
||||
.map(|(element, spread)| {
|
||||
let element = element.get().into();
|
||||
if *spread {
|
||||
SpreadOrExpression::Spread(element)
|
||||
} else {
|
||||
SpreadOrExpression::Expression(element)
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
224
asg/src/expression/array_range_access.rs
Normal file
224
asg/src/expression/array_range_access.rs
Normal file
@ -0,0 +1,224 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{AsgConvertError, ConstValue, Expression, ExpressionNode, FromAst, Node, PartialType, Scope, Span, Type};
|
||||
use leo_ast::IntegerType;
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ArrayRangeAccessExpression<'a> {
|
||||
pub parent: Cell<Option<&'a Expression<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub array: Cell<&'a Expression<'a>>,
|
||||
pub left: Cell<Option<&'a Expression<'a>>>,
|
||||
pub right: Cell<Option<&'a Expression<'a>>>,
|
||||
// this is either const(right) - const(left) OR the length inferred by type checking
|
||||
// special attention must be made to update this if semantic-altering changes are made to left or right.
|
||||
pub length: usize,
|
||||
}
|
||||
|
||||
impl<'a> Node for ArrayRangeAccessExpression<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExpressionNode<'a> for ArrayRangeAccessExpression<'a> {
|
||||
fn set_parent(&self, parent: &'a Expression<'a>) {
|
||||
self.parent.replace(Some(parent));
|
||||
}
|
||||
|
||||
fn get_parent(&self) -> Option<&'a Expression<'a>> {
|
||||
self.parent.get()
|
||||
}
|
||||
|
||||
fn enforce_parents(&self, expr: &'a Expression<'a>) {
|
||||
self.array.get().set_parent(expr);
|
||||
self.array.get().enforce_parents(self.array.get());
|
||||
if let Some(left) = self.left.get() {
|
||||
left.set_parent(expr);
|
||||
}
|
||||
if let Some(right) = self.right.get() {
|
||||
right.set_parent(expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_type(&self) -> Option<Type<'a>> {
|
||||
let element = match self.array.get().get_type() {
|
||||
Some(Type::Array(element, _)) => element,
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(Type::Array(element, self.length))
|
||||
}
|
||||
|
||||
fn is_mut_ref(&self) -> bool {
|
||||
self.array.get().is_mut_ref()
|
||||
}
|
||||
|
||||
fn const_value(&self) -> Option<ConstValue> {
|
||||
let mut array = match self.array.get().const_value()? {
|
||||
ConstValue::Array(values) => values,
|
||||
_ => return None,
|
||||
};
|
||||
let const_left = match self.left.get().map(|x| x.const_value()) {
|
||||
Some(Some(ConstValue::Int(x))) => x.to_usize()?,
|
||||
None => 0,
|
||||
_ => return None,
|
||||
};
|
||||
let const_right = match self.right.get().map(|x| x.const_value()) {
|
||||
Some(Some(ConstValue::Int(x))) => x.to_usize()?,
|
||||
None => array.len(),
|
||||
_ => return None,
|
||||
};
|
||||
if const_left > const_right || const_right as usize > array.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(ConstValue::Array(array.drain(const_left..const_right).collect()))
|
||||
}
|
||||
|
||||
fn is_consty(&self) -> bool {
|
||||
self.array.get().is_consty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::ArrayRangeAccessExpression> for ArrayRangeAccessExpression<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &leo_ast::ArrayRangeAccessExpression,
|
||||
expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<ArrayRangeAccessExpression<'a>, AsgConvertError> {
|
||||
let (expected_array, expected_len) = match expected_type.clone() {
|
||||
Some(PartialType::Array(element, len)) => (Some(PartialType::Array(element, None)), len),
|
||||
None => (None, None),
|
||||
Some(x) => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&x.to_string(),
|
||||
Some("array"),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
};
|
||||
let array = <&Expression<'a>>::from_ast(scope, &*value.array, expected_array)?;
|
||||
let array_type = array.get_type();
|
||||
let (parent_element, parent_size) = match array_type {
|
||||
Some(Type::Array(inner, size)) => (inner, size),
|
||||
type_ => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
"array",
|
||||
type_.map(|x| x.to_string()).as_deref(),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
let left = value
|
||||
.left
|
||||
.as_deref()
|
||||
.map(|left| {
|
||||
<&Expression<'a>>::from_ast(scope, left, Some(PartialType::Integer(None, Some(IntegerType::U32))))
|
||||
})
|
||||
.transpose()?;
|
||||
let right = value
|
||||
.right
|
||||
.as_deref()
|
||||
.map(|right| {
|
||||
<&Expression<'a>>::from_ast(scope, right, Some(PartialType::Integer(None, Some(IntegerType::U32))))
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
let const_left = match left.map(|x| x.const_value()) {
|
||||
Some(Some(ConstValue::Int(x))) => x.to_usize(),
|
||||
None => Some(0),
|
||||
_ => None,
|
||||
};
|
||||
let const_right = match right.map(|x| x.const_value()) {
|
||||
Some(Some(ConstValue::Int(value))) => {
|
||||
let value = value.to_usize();
|
||||
if let Some(value) = value {
|
||||
if value > parent_size {
|
||||
return Err(AsgConvertError::array_index_out_of_bounds(
|
||||
value,
|
||||
&right.unwrap().span().cloned().unwrap_or_default(),
|
||||
));
|
||||
} else if let Some(left) = const_left {
|
||||
if left > value {
|
||||
return Err(AsgConvertError::array_index_out_of_bounds(
|
||||
value,
|
||||
&right.unwrap().span().cloned().unwrap_or_default(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
value
|
||||
}
|
||||
None => Some(parent_size),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let mut length = if let (Some(left), Some(right)) = (const_left, const_right) {
|
||||
Some(right - left)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(expected_len) = expected_len {
|
||||
if let Some(length) = length {
|
||||
if length != expected_len {
|
||||
let concrete_type = Type::Array(parent_element, length);
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&expected_type.as_ref().unwrap().to_string(),
|
||||
Some(&concrete_type.to_string()),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(value) = const_left {
|
||||
if value + expected_len > parent_size {
|
||||
return Err(AsgConvertError::array_index_out_of_bounds(
|
||||
value,
|
||||
&left.unwrap().span().cloned().unwrap_or_default(),
|
||||
));
|
||||
}
|
||||
}
|
||||
length = Some(expected_len);
|
||||
}
|
||||
if length.is_none() {
|
||||
return Err(AsgConvertError::unknown_array_size(&value.span));
|
||||
}
|
||||
|
||||
Ok(ArrayRangeAccessExpression {
|
||||
parent: Cell::new(None),
|
||||
span: Some(value.span.clone()),
|
||||
array: Cell::new(array),
|
||||
left: Cell::new(left),
|
||||
right: Cell::new(right),
|
||||
length: length.unwrap(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::ArrayRangeAccessExpression> for &ArrayRangeAccessExpression<'a> {
|
||||
fn into(self) -> leo_ast::ArrayRangeAccessExpression {
|
||||
leo_ast::ArrayRangeAccessExpression {
|
||||
array: Box::new(self.array.get().into()),
|
||||
left: self.left.get().map(|left| Box::new(left.into())),
|
||||
right: self.right.get().map(|right| Box::new(right.into())),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
262
asg/src/expression/binary.rs
Normal file
262
asg/src/expression/binary.rs
Normal file
@ -0,0 +1,262 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{AsgConvertError, ConstValue, Expression, ExpressionNode, FromAst, Node, PartialType, Scope, Span, Type};
|
||||
pub use leo_ast::{BinaryOperation, BinaryOperationClass};
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BinaryExpression<'a> {
|
||||
pub parent: Cell<Option<&'a Expression<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub operation: BinaryOperation,
|
||||
pub left: Cell<&'a Expression<'a>>,
|
||||
pub right: Cell<&'a Expression<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Node for BinaryExpression<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExpressionNode<'a> for BinaryExpression<'a> {
|
||||
fn set_parent(&self, parent: &'a Expression<'a>) {
|
||||
self.parent.replace(Some(parent));
|
||||
}
|
||||
|
||||
fn get_parent(&self) -> Option<&'a Expression<'a>> {
|
||||
self.parent.get()
|
||||
}
|
||||
|
||||
fn enforce_parents(&self, expr: &'a Expression<'a>) {
|
||||
self.left.get().set_parent(expr);
|
||||
self.right.get().set_parent(expr);
|
||||
}
|
||||
|
||||
fn get_type(&self) -> Option<Type<'a>> {
|
||||
match self.operation.class() {
|
||||
BinaryOperationClass::Boolean => Some(Type::Boolean),
|
||||
BinaryOperationClass::Numeric => self.left.get().get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_mut_ref(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn const_value(&self) -> Option<ConstValue> {
|
||||
use BinaryOperation::*;
|
||||
let left = self.left.get().const_value()?;
|
||||
let right = self.right.get().const_value()?;
|
||||
|
||||
match (left, right) {
|
||||
(ConstValue::Int(left), ConstValue::Int(right)) => Some(match self.operation {
|
||||
Add => ConstValue::Int(left.value_add(&right)?),
|
||||
Sub => ConstValue::Int(left.value_sub(&right)?),
|
||||
Mul => ConstValue::Int(left.value_mul(&right)?),
|
||||
Div => ConstValue::Int(left.value_div(&right)?),
|
||||
Pow => ConstValue::Int(left.value_pow(&right)?),
|
||||
Eq => ConstValue::Boolean(left == right),
|
||||
Ne => ConstValue::Boolean(left != right),
|
||||
Ge => ConstValue::Boolean(left.value_ge(&right)?),
|
||||
Gt => ConstValue::Boolean(left.value_gt(&right)?),
|
||||
Le => ConstValue::Boolean(left.value_le(&right)?),
|
||||
Lt => ConstValue::Boolean(left.value_lt(&right)?),
|
||||
_ => return None,
|
||||
}),
|
||||
// (ConstValue::Field(left), ConstValue::Field(right)) => {
|
||||
// Some(match self.operation {
|
||||
// Add => ConstValue::Field(left.checked_add(&right)?),
|
||||
// Sub => ConstValue::Field(left.checked_sub(&right)?),
|
||||
// Mul => ConstValue::Field(left.checked_mul(&right)?),
|
||||
// Div => ConstValue::Field(left.checked_div(&right)?),
|
||||
// Eq => ConstValue::Boolean(left == right),
|
||||
// Ne => ConstValue::Boolean(left != right),
|
||||
// _ => return None,
|
||||
// })
|
||||
// },
|
||||
(ConstValue::Boolean(left), ConstValue::Boolean(right)) => Some(match self.operation {
|
||||
Eq => ConstValue::Boolean(left == right),
|
||||
Ne => ConstValue::Boolean(left != right),
|
||||
And => ConstValue::Boolean(left && right),
|
||||
Or => ConstValue::Boolean(left || right),
|
||||
_ => return None,
|
||||
}),
|
||||
//todo: group?
|
||||
(left, right) => Some(match self.operation {
|
||||
Eq => ConstValue::Boolean(left == right),
|
||||
Ne => ConstValue::Boolean(left != right),
|
||||
_ => return None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_consty(&self) -> bool {
|
||||
self.left.get().is_consty() && self.right.get().is_consty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::BinaryExpression> for BinaryExpression<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &leo_ast::BinaryExpression,
|
||||
expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<BinaryExpression<'a>, AsgConvertError> {
|
||||
let class = value.op.class();
|
||||
let expected_type = match class {
|
||||
BinaryOperationClass::Boolean => match expected_type {
|
||||
Some(PartialType::Type(Type::Boolean)) | None => None,
|
||||
Some(x) => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&x.to_string(),
|
||||
Some(&*Type::Boolean.to_string()),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
},
|
||||
BinaryOperationClass::Numeric => match expected_type {
|
||||
Some(x @ PartialType::Integer(_, _)) => Some(x),
|
||||
Some(x @ PartialType::Type(Type::Field)) => Some(x),
|
||||
Some(x @ PartialType::Type(Type::Group)) => Some(x),
|
||||
Some(x) => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&x.to_string(),
|
||||
Some("integer, field, or group"),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
None => None,
|
||||
},
|
||||
};
|
||||
|
||||
// left
|
||||
let (left, right) = match <&Expression<'a>>::from_ast(scope, &*value.left, expected_type.clone()) {
|
||||
Ok(left) => {
|
||||
if let Some(left_type) = left.get_type() {
|
||||
let right = <&Expression<'a>>::from_ast(scope, &*value.right, Some(left_type.partial()))?;
|
||||
(left, right)
|
||||
} else {
|
||||
let right = <&Expression<'a>>::from_ast(scope, &*value.right, expected_type)?;
|
||||
if let Some(right_type) = right.get_type() {
|
||||
(
|
||||
<&Expression<'a>>::from_ast(scope, &*value.left, Some(right_type.partial()))?,
|
||||
right,
|
||||
)
|
||||
} else {
|
||||
(left, right)
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
let right = <&Expression<'a>>::from_ast(scope, &*value.right, expected_type)?;
|
||||
if let Some(right_type) = right.get_type() {
|
||||
(
|
||||
<&Expression<'a>>::from_ast(scope, &*value.left, Some(right_type.partial()))?,
|
||||
right,
|
||||
)
|
||||
} else {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let left_type = left.get_type();
|
||||
#[allow(clippy::unused_unit)]
|
||||
match class {
|
||||
BinaryOperationClass::Numeric => match left_type {
|
||||
Some(Type::Integer(_)) => (),
|
||||
Some(Type::Group) | Some(Type::Field)
|
||||
if value.op == BinaryOperation::Add || value.op == BinaryOperation::Sub =>
|
||||
{
|
||||
()
|
||||
}
|
||||
Some(Type::Field) if value.op == BinaryOperation::Mul || value.op == BinaryOperation::Div => (),
|
||||
type_ => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
"integer",
|
||||
type_.map(|x| x.to_string()).as_deref(),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
},
|
||||
BinaryOperationClass::Boolean => match &value.op {
|
||||
BinaryOperation::And | BinaryOperation::Or => match left_type {
|
||||
Some(Type::Boolean) | None => (),
|
||||
Some(x) => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&x.to_string(),
|
||||
Some(&*Type::Boolean.to_string()),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
},
|
||||
BinaryOperation::Eq | BinaryOperation::Ne => (), // all types allowed
|
||||
_ => match left_type {
|
||||
Some(Type::Integer(_)) | None => (),
|
||||
Some(x) => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&x.to_string(),
|
||||
Some("integer"),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
let right_type = right.get_type();
|
||||
|
||||
match (left_type, right_type) {
|
||||
(Some(left_type), Some(right_type)) => {
|
||||
if !left_type.is_assignable_from(&right_type) {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&left_type.to_string(),
|
||||
Some(&*right_type.to_string()),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
(None, None) => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
"any type",
|
||||
Some("unknown type"),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
(_, _) => (),
|
||||
}
|
||||
Ok(BinaryExpression {
|
||||
parent: Cell::new(None),
|
||||
span: Some(value.span.clone()),
|
||||
operation: value.op.clone(),
|
||||
left: Cell::new(left),
|
||||
right: Cell::new(right),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::BinaryExpression> for &BinaryExpression<'a> {
|
||||
fn into(self) -> leo_ast::BinaryExpression {
|
||||
leo_ast::BinaryExpression {
|
||||
op: self.operation.clone(),
|
||||
left: Box::new(self.left.get().into()),
|
||||
right: Box::new(self.right.get().into()),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
250
asg/src/expression/call.rs
Normal file
250
asg/src/expression/call.rs
Normal file
@ -0,0 +1,250 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
AsgConvertError,
|
||||
CircuitMember,
|
||||
ConstValue,
|
||||
Expression,
|
||||
ExpressionNode,
|
||||
FromAst,
|
||||
Function,
|
||||
FunctionQualifier,
|
||||
Node,
|
||||
PartialType,
|
||||
Scope,
|
||||
Span,
|
||||
Type,
|
||||
};
|
||||
pub use leo_ast::{BinaryOperation, Node as AstNode};
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CallExpression<'a> {
|
||||
pub parent: Cell<Option<&'a Expression<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub function: Cell<&'a Function<'a>>,
|
||||
pub target: Cell<Option<&'a Expression<'a>>>,
|
||||
pub arguments: Vec<Cell<&'a Expression<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a> Node for CallExpression<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExpressionNode<'a> for CallExpression<'a> {
|
||||
fn set_parent(&self, parent: &'a Expression<'a>) {
|
||||
self.parent.replace(Some(parent));
|
||||
}
|
||||
|
||||
fn get_parent(&self) -> Option<&'a Expression<'a>> {
|
||||
self.parent.get()
|
||||
}
|
||||
|
||||
fn enforce_parents(&self, expr: &'a Expression<'a>) {
|
||||
if let Some(target) = self.target.get() {
|
||||
target.set_parent(expr);
|
||||
}
|
||||
self.arguments.iter().for_each(|element| {
|
||||
element.get().set_parent(expr);
|
||||
})
|
||||
}
|
||||
|
||||
fn get_type(&self) -> Option<Type<'a>> {
|
||||
Some(self.function.get().output.clone())
|
||||
}
|
||||
|
||||
fn is_mut_ref(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn const_value(&self) -> Option<ConstValue> {
|
||||
// static function const evaluation
|
||||
None
|
||||
}
|
||||
|
||||
fn is_consty(&self) -> bool {
|
||||
self.target.get().map(|x| x.is_consty()).unwrap_or(true) && self.arguments.iter().all(|x| x.get().is_consty())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::CallExpression> for CallExpression<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &leo_ast::CallExpression,
|
||||
expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<CallExpression<'a>, AsgConvertError> {
|
||||
let (target, function) = match &*value.function {
|
||||
leo_ast::Expression::Identifier(name) => (
|
||||
None,
|
||||
scope
|
||||
.resolve_function(&name.name)
|
||||
.ok_or_else(|| AsgConvertError::unresolved_function(&name.name, &name.span))?,
|
||||
),
|
||||
leo_ast::Expression::CircuitMemberAccess(leo_ast::CircuitMemberAccessExpression {
|
||||
circuit: ast_circuit,
|
||||
name,
|
||||
span,
|
||||
}) => {
|
||||
let target = <&Expression<'a>>::from_ast(scope, &**ast_circuit, None)?;
|
||||
let circuit = match target.get_type() {
|
||||
Some(Type::Circuit(circuit)) => circuit,
|
||||
type_ => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
"circuit",
|
||||
type_.map(|x| x.to_string()).as_deref(),
|
||||
span,
|
||||
));
|
||||
}
|
||||
};
|
||||
let circuit_name = circuit.name.borrow().name.clone();
|
||||
let member = circuit.members.borrow();
|
||||
let member = member
|
||||
.get(name.name.as_ref())
|
||||
.ok_or_else(|| AsgConvertError::unresolved_circuit_member(&circuit_name, &name.name, span))?;
|
||||
match member {
|
||||
CircuitMember::Function(body) => {
|
||||
if body.qualifier == FunctionQualifier::Static {
|
||||
return Err(AsgConvertError::circuit_static_call_invalid(
|
||||
&circuit_name,
|
||||
&name.name,
|
||||
span,
|
||||
));
|
||||
} else if body.qualifier == FunctionQualifier::MutSelfRef && !target.is_mut_ref() {
|
||||
return Err(AsgConvertError::circuit_member_mut_call_invalid(
|
||||
&circuit_name,
|
||||
&name.name,
|
||||
span,
|
||||
));
|
||||
}
|
||||
(Some(target), *body)
|
||||
}
|
||||
CircuitMember::Variable(_) => {
|
||||
return Err(AsgConvertError::circuit_variable_call(&circuit_name, &name.name, span));
|
||||
}
|
||||
}
|
||||
}
|
||||
leo_ast::Expression::CircuitStaticFunctionAccess(leo_ast::CircuitStaticFunctionAccessExpression {
|
||||
circuit: ast_circuit,
|
||||
name,
|
||||
span,
|
||||
}) => {
|
||||
let circuit = if let leo_ast::Expression::Identifier(circuit_name) = &**ast_circuit {
|
||||
scope
|
||||
.resolve_circuit(&circuit_name.name)
|
||||
.ok_or_else(|| AsgConvertError::unresolved_circuit(&circuit_name.name, &circuit_name.span))?
|
||||
} else {
|
||||
return Err(AsgConvertError::unexpected_type("circuit", None, span));
|
||||
};
|
||||
let circuit_name = circuit.name.borrow().name.clone();
|
||||
|
||||
let member = circuit.members.borrow();
|
||||
let member = member
|
||||
.get(name.name.as_ref())
|
||||
.ok_or_else(|| AsgConvertError::unresolved_circuit_member(&circuit_name, &name.name, span))?;
|
||||
match member {
|
||||
CircuitMember::Function(body) => {
|
||||
if body.qualifier != FunctionQualifier::Static {
|
||||
return Err(AsgConvertError::circuit_member_call_invalid(
|
||||
&circuit_name,
|
||||
&name.name,
|
||||
span,
|
||||
));
|
||||
}
|
||||
(None, *body)
|
||||
}
|
||||
CircuitMember::Variable(_) => {
|
||||
return Err(AsgConvertError::circuit_variable_call(&circuit_name, &name.name, span));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(AsgConvertError::illegal_ast_structure(
|
||||
"non Identifier/CircuitMemberAccess/CircuitStaticFunctionAccess as call target",
|
||||
));
|
||||
}
|
||||
};
|
||||
if let Some(expected) = expected_type {
|
||||
let output: Type = function.output.clone();
|
||||
if !expected.matches(&output) {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&expected.to_string(),
|
||||
Some(&*output.to_string()),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
if value.arguments.len() != function.arguments.len() {
|
||||
return Err(AsgConvertError::unexpected_call_argument_count(
|
||||
function.arguments.len(),
|
||||
value.arguments.len(),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
|
||||
let arguments = value
|
||||
.arguments
|
||||
.iter()
|
||||
.zip(function.arguments.iter())
|
||||
.map(|(expr, (_, argument))| {
|
||||
let argument = argument.get().borrow();
|
||||
let converted = <&Expression<'a>>::from_ast(scope, expr, Some(argument.type_.clone().partial()))?;
|
||||
if argument.const_ && !converted.is_consty() {
|
||||
return Err(AsgConvertError::unexpected_nonconst(expr.span()));
|
||||
}
|
||||
Ok(Cell::new(converted))
|
||||
})
|
||||
.collect::<Result<Vec<_>, AsgConvertError>>()?;
|
||||
|
||||
if function.is_test() {
|
||||
return Err(AsgConvertError::call_test_function(&value.span));
|
||||
}
|
||||
Ok(CallExpression {
|
||||
parent: Cell::new(None),
|
||||
span: Some(value.span.clone()),
|
||||
arguments,
|
||||
function: Cell::new(function),
|
||||
target: Cell::new(target),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::CallExpression> for &CallExpression<'a> {
|
||||
fn into(self) -> leo_ast::CallExpression {
|
||||
let target_function = if let Some(target) = self.target.get() {
|
||||
target.into()
|
||||
} else {
|
||||
let circuit = self.function.get().circuit.get();
|
||||
if let Some(circuit) = circuit {
|
||||
leo_ast::Expression::CircuitStaticFunctionAccess(leo_ast::CircuitStaticFunctionAccessExpression {
|
||||
circuit: Box::new(leo_ast::Expression::Identifier(circuit.name.borrow().clone())),
|
||||
name: self.function.get().name.borrow().clone(),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
})
|
||||
} else {
|
||||
leo_ast::Expression::Identifier(self.function.get().name.borrow().clone())
|
||||
}
|
||||
};
|
||||
leo_ast::CallExpression {
|
||||
function: Box::new(target_function),
|
||||
arguments: self.arguments.iter().map(|arg| arg.get().into()).collect(),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
109
asg/src/expression/cast.rs
Normal file
109
asg/src/expression/cast.rs
Normal file
@ -0,0 +1,109 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{AsgConvertError, ConstValue, Expression, ExpressionNode, FromAst, Node, PartialType, Scope, Span, Type};
|
||||
pub use leo_ast::UnaryOperation;
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CastExpression<'a> {
|
||||
pub parent: Cell<Option<&'a Expression<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub inner: Cell<&'a Expression<'a>>,
|
||||
pub target_type: Type<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Node for CastExpression<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExpressionNode<'a> for CastExpression<'a> {
|
||||
fn set_parent(&self, parent: &'a Expression<'a>) {
|
||||
self.parent.replace(Some(parent));
|
||||
}
|
||||
|
||||
fn get_parent(&self) -> Option<&'a Expression<'a>> {
|
||||
self.parent.get()
|
||||
}
|
||||
|
||||
fn enforce_parents(&self, expr: &'a Expression<'a>) {
|
||||
self.inner.get().set_parent(expr);
|
||||
}
|
||||
|
||||
fn get_type(&self) -> Option<Type<'a>> {
|
||||
Some(self.target_type.clone())
|
||||
}
|
||||
|
||||
fn is_mut_ref(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn const_value(&self) -> Option<ConstValue> {
|
||||
let value = self.inner.get().const_value()?;
|
||||
match value {
|
||||
ConstValue::Int(int) => match &self.target_type {
|
||||
Type::Integer(target) => Some(ConstValue::Int(int.cast_to(target))),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_consty(&self) -> bool {
|
||||
self.inner.get().is_consty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::CastExpression> for CastExpression<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &leo_ast::CastExpression,
|
||||
expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<CastExpression<'a>, AsgConvertError> {
|
||||
let target_type = scope.resolve_ast_type(&value.target_type)?;
|
||||
if let Some(expected_type) = &expected_type {
|
||||
if !expected_type.matches(&target_type) {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&expected_type.to_string(),
|
||||
Some(&target_type.to_string()),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let inner = <&Expression<'a>>::from_ast(scope, &*value.inner, None)?;
|
||||
|
||||
Ok(CastExpression {
|
||||
parent: Cell::new(None),
|
||||
span: Some(value.span.clone()),
|
||||
inner: Cell::new(inner),
|
||||
target_type,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::CastExpression> for &CastExpression<'a> {
|
||||
fn into(self) -> leo_ast::CastExpression {
|
||||
leo_ast::CastExpression {
|
||||
target_type: (&self.target_type).into(),
|
||||
inner: Box::new(self.inner.get().into()),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
233
asg/src/expression/circuit_access.rs
Normal file
233
asg/src/expression/circuit_access.rs
Normal file
@ -0,0 +1,233 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
AsgConvertError,
|
||||
Circuit,
|
||||
CircuitMember,
|
||||
ConstValue,
|
||||
Expression,
|
||||
ExpressionNode,
|
||||
FromAst,
|
||||
Identifier,
|
||||
Node,
|
||||
PartialType,
|
||||
Scope,
|
||||
Span,
|
||||
Type,
|
||||
};
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CircuitAccessExpression<'a> {
|
||||
pub parent: Cell<Option<&'a Expression<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub circuit: Cell<&'a Circuit<'a>>,
|
||||
pub target: Cell<Option<&'a Expression<'a>>>,
|
||||
pub member: Identifier,
|
||||
}
|
||||
|
||||
impl<'a> Node for CircuitAccessExpression<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExpressionNode<'a> for CircuitAccessExpression<'a> {
|
||||
fn set_parent(&self, parent: &'a Expression<'a>) {
|
||||
self.parent.replace(Some(parent));
|
||||
}
|
||||
|
||||
fn get_parent(&self) -> Option<&'a Expression<'a>> {
|
||||
self.parent.get()
|
||||
}
|
||||
|
||||
fn enforce_parents(&self, expr: &'a Expression<'a>) {
|
||||
if let Some(target) = self.target.get() {
|
||||
target.set_parent(expr);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_type(&self) -> Option<Type<'a>> {
|
||||
if self.target.get().is_none() {
|
||||
None // function target only for static
|
||||
} else {
|
||||
let members = self.circuit.get().members.borrow();
|
||||
let member = members.get(self.member.name.as_ref())?;
|
||||
match member {
|
||||
CircuitMember::Variable(type_) => Some(type_.clone()),
|
||||
CircuitMember::Function(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_mut_ref(&self) -> bool {
|
||||
if let Some(target) = self.target.get() {
|
||||
target.is_mut_ref()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fn const_value(&self) -> Option<ConstValue> {
|
||||
None
|
||||
}
|
||||
|
||||
fn is_consty(&self) -> bool {
|
||||
self.target.get().map(|x| x.is_consty()).unwrap_or(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::CircuitMemberAccessExpression> for CircuitAccessExpression<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &leo_ast::CircuitMemberAccessExpression,
|
||||
expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<CircuitAccessExpression<'a>, AsgConvertError> {
|
||||
let target = <&'a Expression<'a>>::from_ast(scope, &*value.circuit, None)?;
|
||||
let circuit = match target.get_type() {
|
||||
Some(Type::Circuit(circuit)) => circuit,
|
||||
x => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
"circuit",
|
||||
x.map(|x| x.to_string()).as_deref(),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// scoping refcell reference
|
||||
let found_member = {
|
||||
if let Some(member) = circuit.members.borrow().get(value.name.name.as_ref()) {
|
||||
if let Some(expected_type) = &expected_type {
|
||||
if let CircuitMember::Variable(type_) = &member {
|
||||
let type_: Type = type_.clone();
|
||||
if !expected_type.matches(&type_) {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&expected_type.to_string(),
|
||||
Some(&type_.to_string()),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
} // used by call expression
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
if found_member {
|
||||
// skip
|
||||
} else if circuit.is_input_pseudo_circuit() {
|
||||
// add new member to implicit input
|
||||
if let Some(expected_type) = expected_type.map(PartialType::full).flatten() {
|
||||
circuit.members.borrow_mut().insert(
|
||||
value.name.name.to_string(),
|
||||
CircuitMember::Variable(expected_type.clone()),
|
||||
);
|
||||
} else {
|
||||
return Err(AsgConvertError::input_ref_needs_type(
|
||||
&circuit.name.borrow().name,
|
||||
&value.name.name,
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(AsgConvertError::unresolved_circuit_member(
|
||||
&circuit.name.borrow().name,
|
||||
&value.name.name,
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(CircuitAccessExpression {
|
||||
parent: Cell::new(None),
|
||||
span: Some(value.span.clone()),
|
||||
target: Cell::new(Some(target)),
|
||||
circuit: Cell::new(circuit),
|
||||
member: value.name.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::CircuitStaticFunctionAccessExpression> for CircuitAccessExpression<'a> {
|
||||
fn from_ast(
|
||||
scope: &Scope<'a>,
|
||||
value: &leo_ast::CircuitStaticFunctionAccessExpression,
|
||||
expected_type: Option<PartialType>,
|
||||
) -> Result<CircuitAccessExpression<'a>, AsgConvertError> {
|
||||
let circuit = match &*value.circuit {
|
||||
leo_ast::Expression::Identifier(name) => scope
|
||||
.resolve_circuit(&name.name)
|
||||
.ok_or_else(|| AsgConvertError::unresolved_circuit(&name.name, &name.span))?,
|
||||
_ => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
"circuit",
|
||||
Some("unknown"),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(expected_type) = expected_type {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&expected_type.to_string(),
|
||||
Some("none"),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(CircuitMember::Function(_)) = circuit.members.borrow().get(value.name.name.as_ref()) {
|
||||
// okay
|
||||
} else {
|
||||
return Err(AsgConvertError::unresolved_circuit_member(
|
||||
&circuit.name.borrow().name,
|
||||
&value.name.name,
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(CircuitAccessExpression {
|
||||
parent: Cell::new(None),
|
||||
span: Some(value.span.clone()),
|
||||
target: Cell::new(None),
|
||||
circuit: Cell::new(circuit),
|
||||
member: value.name.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::Expression> for &CircuitAccessExpression<'a> {
|
||||
fn into(self) -> leo_ast::Expression {
|
||||
if let Some(target) = self.target.get() {
|
||||
leo_ast::Expression::CircuitMemberAccess(leo_ast::CircuitMemberAccessExpression {
|
||||
circuit: Box::new(target.into()),
|
||||
name: self.member.clone(),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
})
|
||||
} else {
|
||||
leo_ast::Expression::CircuitStaticFunctionAccess(leo_ast::CircuitStaticFunctionAccessExpression {
|
||||
circuit: Box::new(leo_ast::Expression::Identifier(
|
||||
self.circuit.get().name.borrow().clone(),
|
||||
)),
|
||||
name: self.member.clone(),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
182
asg/src/expression/circuit_init.rs
Normal file
182
asg/src/expression/circuit_init.rs
Normal file
@ -0,0 +1,182 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
AsgConvertError,
|
||||
Circuit,
|
||||
CircuitMember,
|
||||
ConstValue,
|
||||
Expression,
|
||||
ExpressionNode,
|
||||
FromAst,
|
||||
Identifier,
|
||||
Node,
|
||||
PartialType,
|
||||
Scope,
|
||||
Span,
|
||||
Type,
|
||||
};
|
||||
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CircuitInitExpression<'a> {
|
||||
pub parent: Cell<Option<&'a Expression<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub circuit: Cell<&'a Circuit<'a>>,
|
||||
pub values: Vec<(Identifier, Cell<&'a Expression<'a>>)>,
|
||||
}
|
||||
|
||||
impl<'a> Node for CircuitInitExpression<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExpressionNode<'a> for CircuitInitExpression<'a> {
|
||||
fn set_parent(&self, parent: &'a Expression<'a>) {
|
||||
self.parent.replace(Some(parent));
|
||||
}
|
||||
|
||||
fn get_parent(&self) -> Option<&'a Expression<'a>> {
|
||||
self.parent.get()
|
||||
}
|
||||
|
||||
fn enforce_parents(&self, expr: &'a Expression<'a>) {
|
||||
self.values.iter().for_each(|(_, element)| {
|
||||
element.get().set_parent(expr);
|
||||
})
|
||||
}
|
||||
|
||||
fn get_type(&self) -> Option<Type<'a>> {
|
||||
Some(Type::Circuit(self.circuit.get()))
|
||||
}
|
||||
|
||||
fn is_mut_ref(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn const_value(&self) -> Option<ConstValue> {
|
||||
None
|
||||
}
|
||||
|
||||
fn is_consty(&self) -> bool {
|
||||
self.values.iter().all(|(_, value)| value.get().is_consty())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::CircuitInitExpression> for CircuitInitExpression<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &leo_ast::CircuitInitExpression,
|
||||
expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<CircuitInitExpression<'a>, AsgConvertError> {
|
||||
let circuit = scope
|
||||
.resolve_circuit(&value.name.name)
|
||||
.ok_or_else(|| AsgConvertError::unresolved_circuit(&value.name.name, &value.name.span))?;
|
||||
match expected_type {
|
||||
Some(PartialType::Type(Type::Circuit(expected_circuit))) if expected_circuit == circuit => (),
|
||||
None => (),
|
||||
Some(x) => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&x.to_string(),
|
||||
Some(&circuit.name.borrow().name),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
let members: IndexMap<&str, (&Identifier, Option<&leo_ast::Expression>)> = value
|
||||
.members
|
||||
.iter()
|
||||
.map(|x| (x.identifier.name.as_ref(), (&x.identifier, x.expression.as_ref())))
|
||||
.collect();
|
||||
|
||||
let mut values: Vec<(Identifier, Cell<&'a Expression<'a>>)> = vec![];
|
||||
let mut defined_variables = IndexSet::<String>::new();
|
||||
|
||||
{
|
||||
let circuit_members = circuit.members.borrow();
|
||||
for (name, member) in circuit_members.iter() {
|
||||
if defined_variables.contains(name) {
|
||||
return Err(AsgConvertError::overridden_circuit_member(
|
||||
&circuit.name.borrow().name,
|
||||
name,
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
defined_variables.insert(name.clone());
|
||||
let type_: Type = if let CircuitMember::Variable(type_) = &member {
|
||||
type_.clone()
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
if let Some((identifier, receiver)) = members.get(&**name) {
|
||||
let received = if let Some(receiver) = *receiver {
|
||||
<&Expression<'a>>::from_ast(scope, receiver, Some(type_.partial()))?
|
||||
} else {
|
||||
<&Expression<'a>>::from_ast(
|
||||
scope,
|
||||
&leo_ast::Expression::Identifier((*identifier).clone()),
|
||||
Some(type_.partial()),
|
||||
)?
|
||||
};
|
||||
values.push(((*identifier).clone(), Cell::new(received)));
|
||||
} else {
|
||||
return Err(AsgConvertError::missing_circuit_member(
|
||||
&circuit.name.borrow().name,
|
||||
name,
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
for (name, (identifier, _expression)) in members.iter() {
|
||||
if circuit_members.get(*name).is_none() {
|
||||
return Err(AsgConvertError::extra_circuit_member(
|
||||
&circuit.name.borrow().name,
|
||||
*name,
|
||||
&identifier.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CircuitInitExpression {
|
||||
parent: Cell::new(None),
|
||||
span: Some(value.span.clone()),
|
||||
circuit: Cell::new(circuit),
|
||||
values,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::CircuitInitExpression> for &CircuitInitExpression<'a> {
|
||||
fn into(self) -> leo_ast::CircuitInitExpression {
|
||||
leo_ast::CircuitInitExpression {
|
||||
name: self.circuit.get().name.borrow().clone(),
|
||||
members: self
|
||||
.values
|
||||
.iter()
|
||||
.map(|(name, value)| leo_ast::CircuitImpliedVariableDefinition {
|
||||
identifier: name.clone(),
|
||||
expression: Some(value.get().into()),
|
||||
})
|
||||
.collect(),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
240
asg/src/expression/constant.rs
Normal file
240
asg/src/expression/constant.rs
Normal file
@ -0,0 +1,240 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
AsgConvertError,
|
||||
ConstInt,
|
||||
ConstValue,
|
||||
Expression,
|
||||
ExpressionNode,
|
||||
FromAst,
|
||||
GroupValue,
|
||||
Node,
|
||||
PartialType,
|
||||
Scope,
|
||||
Span,
|
||||
Type,
|
||||
};
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Constant<'a> {
|
||||
pub parent: Cell<Option<&'a Expression<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub value: ConstValue, // should not be compound constants
|
||||
}
|
||||
|
||||
impl<'a> Node for Constant<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExpressionNode<'a> for Constant<'a> {
|
||||
fn set_parent(&self, parent: &'a Expression<'a>) {
|
||||
self.parent.replace(Some(parent));
|
||||
}
|
||||
|
||||
fn get_parent(&self) -> Option<&'a Expression<'a>> {
|
||||
self.parent.get()
|
||||
}
|
||||
|
||||
fn enforce_parents(&self, _expr: &'a Expression<'a>) {}
|
||||
|
||||
fn get_type(&self) -> Option<Type<'a>> {
|
||||
self.value.get_type()
|
||||
}
|
||||
|
||||
fn is_mut_ref(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn const_value(&self) -> Option<ConstValue> {
|
||||
Some(self.value.clone())
|
||||
}
|
||||
|
||||
fn is_consty(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::ValueExpression> for Constant<'a> {
|
||||
fn from_ast(
|
||||
_scope: &'a Scope<'a>,
|
||||
value: &leo_ast::ValueExpression,
|
||||
expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<Constant<'a>, AsgConvertError> {
|
||||
use leo_ast::ValueExpression::*;
|
||||
Ok(match value {
|
||||
Address(value, span) => {
|
||||
match expected_type.map(PartialType::full).flatten() {
|
||||
Some(Type::Address) | None => (),
|
||||
Some(x) => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&x.to_string(),
|
||||
Some(&*Type::Address.to_string()),
|
||||
span,
|
||||
));
|
||||
}
|
||||
}
|
||||
Constant {
|
||||
parent: Cell::new(None),
|
||||
span: Some(span.clone()),
|
||||
value: ConstValue::Address(value.clone()),
|
||||
}
|
||||
}
|
||||
Boolean(value, span) => {
|
||||
match expected_type.map(PartialType::full).flatten() {
|
||||
Some(Type::Boolean) | None => (),
|
||||
Some(x) => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&x.to_string(),
|
||||
Some(&*Type::Boolean.to_string()),
|
||||
span,
|
||||
));
|
||||
}
|
||||
}
|
||||
Constant {
|
||||
parent: Cell::new(None),
|
||||
span: Some(span.clone()),
|
||||
value: ConstValue::Boolean(
|
||||
value
|
||||
.parse::<bool>()
|
||||
.map_err(|_| AsgConvertError::invalid_boolean(&value, span))?,
|
||||
),
|
||||
}
|
||||
}
|
||||
Field(value, span) => {
|
||||
match expected_type.map(PartialType::full).flatten() {
|
||||
Some(Type::Field) | None => (),
|
||||
Some(x) => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&x.to_string(),
|
||||
Some(&*Type::Field.to_string()),
|
||||
span,
|
||||
));
|
||||
}
|
||||
}
|
||||
Constant {
|
||||
parent: Cell::new(None),
|
||||
span: Some(span.clone()),
|
||||
value: ConstValue::Field(value.parse().map_err(|_| AsgConvertError::invalid_int(&value, span))?),
|
||||
}
|
||||
}
|
||||
Group(value) => {
|
||||
match expected_type.map(PartialType::full).flatten() {
|
||||
Some(Type::Group) | None => (),
|
||||
Some(x) => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&x.to_string(),
|
||||
Some(&*Type::Group.to_string()),
|
||||
value.span(),
|
||||
));
|
||||
}
|
||||
}
|
||||
Constant {
|
||||
parent: Cell::new(None),
|
||||
span: Some(value.span().clone()),
|
||||
value: ConstValue::Group(match &**value {
|
||||
leo_ast::GroupValue::Single(value, _) => GroupValue::Single(value.clone()),
|
||||
leo_ast::GroupValue::Tuple(leo_ast::GroupTuple { x, y, .. }) => {
|
||||
GroupValue::Tuple(x.into(), y.into())
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
Implicit(value, span) => match expected_type {
|
||||
None => return Err(AsgConvertError::unresolved_type("unknown", span)),
|
||||
Some(PartialType::Integer(Some(sub_type), _)) | Some(PartialType::Integer(None, Some(sub_type))) => {
|
||||
Constant {
|
||||
parent: Cell::new(None),
|
||||
span: Some(span.clone()),
|
||||
value: ConstValue::Int(ConstInt::parse(&sub_type, value, span)?),
|
||||
}
|
||||
}
|
||||
Some(PartialType::Type(Type::Field)) => Constant {
|
||||
parent: Cell::new(None),
|
||||
span: Some(span.clone()),
|
||||
value: ConstValue::Field(value.parse().map_err(|_| AsgConvertError::invalid_int(&value, span))?),
|
||||
},
|
||||
Some(PartialType::Type(Type::Group)) => Constant {
|
||||
parent: Cell::new(None),
|
||||
span: Some(span.clone()),
|
||||
value: ConstValue::Group(GroupValue::Single(value.clone())),
|
||||
},
|
||||
Some(PartialType::Type(Type::Address)) => Constant {
|
||||
parent: Cell::new(None),
|
||||
span: Some(span.clone()),
|
||||
value: ConstValue::Address(value.clone()),
|
||||
},
|
||||
Some(x) => return Err(AsgConvertError::unexpected_type(&x.to_string(), Some("unknown"), span)),
|
||||
},
|
||||
Integer(int_type, value, span) => {
|
||||
match expected_type {
|
||||
Some(PartialType::Integer(Some(sub_type), _)) if &sub_type == int_type => (),
|
||||
Some(PartialType::Integer(None, Some(_))) => (),
|
||||
None => (),
|
||||
Some(x) => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&x.to_string(),
|
||||
Some(&*int_type.to_string()),
|
||||
span,
|
||||
));
|
||||
}
|
||||
}
|
||||
Constant {
|
||||
parent: Cell::new(None),
|
||||
span: Some(span.clone()),
|
||||
value: ConstValue::Int(ConstInt::parse(int_type, value, span)?),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::ValueExpression> for &Constant<'a> {
|
||||
fn into(self) -> leo_ast::ValueExpression {
|
||||
match &self.value {
|
||||
ConstValue::Address(value) => {
|
||||
leo_ast::ValueExpression::Address(value.clone(), self.span.clone().unwrap_or_default())
|
||||
}
|
||||
ConstValue::Boolean(value) => {
|
||||
leo_ast::ValueExpression::Boolean(value.to_string().into(), self.span.clone().unwrap_or_default())
|
||||
}
|
||||
ConstValue::Field(value) => {
|
||||
leo_ast::ValueExpression::Field(value.to_string().into(), self.span.clone().unwrap_or_default())
|
||||
}
|
||||
ConstValue::Group(value) => leo_ast::ValueExpression::Group(Box::new(match value {
|
||||
GroupValue::Single(single) => {
|
||||
leo_ast::GroupValue::Single(single.clone(), self.span.clone().unwrap_or_default())
|
||||
}
|
||||
GroupValue::Tuple(left, right) => leo_ast::GroupValue::Tuple(leo_ast::GroupTuple {
|
||||
x: left.into(),
|
||||
y: right.into(),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}),
|
||||
})),
|
||||
ConstValue::Int(int) => leo_ast::ValueExpression::Integer(
|
||||
int.get_int_type(),
|
||||
int.raw_value().into(),
|
||||
self.span.clone().unwrap_or_default(),
|
||||
),
|
||||
ConstValue::Tuple(_) => unimplemented!(),
|
||||
ConstValue::Array(_) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
370
asg/src/expression/mod.rs
Normal file
370
asg/src/expression/mod.rs
Normal file
@ -0,0 +1,370 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! This module defines an expression node in an asg.
|
||||
//!
|
||||
//! Notable differences after conversion from an ast expression include:
|
||||
//! 1. Storing variable references instead of variable identifiers - better history tracking and mutability
|
||||
//! 2. Resolving constant values - optimizes execution of program circuit.
|
||||
|
||||
mod array_access;
|
||||
pub use array_access::*;
|
||||
|
||||
mod array_inline;
|
||||
pub use array_inline::*;
|
||||
|
||||
mod array_init;
|
||||
pub use array_init::*;
|
||||
|
||||
mod array_range_access;
|
||||
pub use array_range_access::*;
|
||||
|
||||
mod binary;
|
||||
pub use binary::*;
|
||||
|
||||
mod call;
|
||||
pub use call::*;
|
||||
|
||||
mod circuit_access;
|
||||
pub use circuit_access::*;
|
||||
|
||||
mod circuit_init;
|
||||
pub use circuit_init::*;
|
||||
|
||||
mod constant;
|
||||
pub use constant::*;
|
||||
|
||||
mod ternary;
|
||||
pub use ternary::*;
|
||||
|
||||
mod tuple_access;
|
||||
pub use tuple_access::*;
|
||||
|
||||
mod tuple_init;
|
||||
pub use tuple_init::*;
|
||||
|
||||
mod unary;
|
||||
pub use unary::*;
|
||||
|
||||
mod variable_ref;
|
||||
pub use variable_ref::*;
|
||||
|
||||
mod cast;
|
||||
pub use cast::*;
|
||||
|
||||
use crate::{AsgConvertError, ConstValue, FromAst, Node, PartialType, Scope, Span, Type};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Expression<'a> {
|
||||
VariableRef(VariableRef<'a>),
|
||||
Constant(Constant<'a>),
|
||||
Binary(BinaryExpression<'a>),
|
||||
Unary(UnaryExpression<'a>),
|
||||
Ternary(TernaryExpression<'a>),
|
||||
Cast(CastExpression<'a>),
|
||||
|
||||
ArrayInline(ArrayInlineExpression<'a>),
|
||||
ArrayInit(ArrayInitExpression<'a>),
|
||||
ArrayAccess(ArrayAccessExpression<'a>),
|
||||
ArrayRangeAccess(ArrayRangeAccessExpression<'a>),
|
||||
|
||||
TupleInit(TupleInitExpression<'a>),
|
||||
TupleAccess(TupleAccessExpression<'a>),
|
||||
|
||||
CircuitInit(CircuitInitExpression<'a>),
|
||||
CircuitAccess(CircuitAccessExpression<'a>),
|
||||
|
||||
Call(CallExpression<'a>),
|
||||
}
|
||||
|
||||
impl<'a> Expression<'a> {
|
||||
pub fn ptr_eq(&self, other: &Expression<'a>) -> bool {
|
||||
std::ptr::eq(self as *const Expression<'a>, other as *const Expression<'a>)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Node for Expression<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
use Expression::*;
|
||||
match self {
|
||||
VariableRef(x) => x.span(),
|
||||
Constant(x) => x.span(),
|
||||
Binary(x) => x.span(),
|
||||
Unary(x) => x.span(),
|
||||
Ternary(x) => x.span(),
|
||||
Cast(x) => x.span(),
|
||||
ArrayInline(x) => x.span(),
|
||||
ArrayInit(x) => x.span(),
|
||||
ArrayAccess(x) => x.span(),
|
||||
ArrayRangeAccess(x) => x.span(),
|
||||
TupleInit(x) => x.span(),
|
||||
TupleAccess(x) => x.span(),
|
||||
CircuitInit(x) => x.span(),
|
||||
CircuitAccess(x) => x.span(),
|
||||
Call(x) => x.span(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ExpressionNode<'a>: Node {
|
||||
fn set_parent(&self, parent: &'a Expression<'a>);
|
||||
fn get_parent(&self) -> Option<&'a Expression<'a>>;
|
||||
fn enforce_parents(&self, expr: &'a Expression<'a>);
|
||||
|
||||
fn get_type(&self) -> Option<Type<'a>>;
|
||||
fn is_mut_ref(&self) -> bool;
|
||||
fn const_value(&self) -> Option<ConstValue>; // todo: memoize
|
||||
fn is_consty(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<'a> ExpressionNode<'a> for Expression<'a> {
|
||||
fn set_parent(&self, parent: &'a Expression<'a>) {
|
||||
use Expression::*;
|
||||
match self {
|
||||
VariableRef(x) => x.set_parent(parent),
|
||||
Constant(x) => x.set_parent(parent),
|
||||
Binary(x) => x.set_parent(parent),
|
||||
Unary(x) => x.set_parent(parent),
|
||||
Ternary(x) => x.set_parent(parent),
|
||||
Cast(x) => x.set_parent(parent),
|
||||
ArrayInline(x) => x.set_parent(parent),
|
||||
ArrayInit(x) => x.set_parent(parent),
|
||||
ArrayAccess(x) => x.set_parent(parent),
|
||||
ArrayRangeAccess(x) => x.set_parent(parent),
|
||||
TupleInit(x) => x.set_parent(parent),
|
||||
TupleAccess(x) => x.set_parent(parent),
|
||||
CircuitInit(x) => x.set_parent(parent),
|
||||
CircuitAccess(x) => x.set_parent(parent),
|
||||
Call(x) => x.set_parent(parent),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_parent(&self) -> Option<&'a Expression<'a>> {
|
||||
use Expression::*;
|
||||
match self {
|
||||
VariableRef(x) => x.get_parent(),
|
||||
Constant(x) => x.get_parent(),
|
||||
Binary(x) => x.get_parent(),
|
||||
Unary(x) => x.get_parent(),
|
||||
Ternary(x) => x.get_parent(),
|
||||
Cast(x) => x.get_parent(),
|
||||
ArrayInline(x) => x.get_parent(),
|
||||
ArrayInit(x) => x.get_parent(),
|
||||
ArrayAccess(x) => x.get_parent(),
|
||||
ArrayRangeAccess(x) => x.get_parent(),
|
||||
TupleInit(x) => x.get_parent(),
|
||||
TupleAccess(x) => x.get_parent(),
|
||||
CircuitInit(x) => x.get_parent(),
|
||||
CircuitAccess(x) => x.get_parent(),
|
||||
Call(x) => x.get_parent(),
|
||||
}
|
||||
}
|
||||
|
||||
fn enforce_parents(&self, expr: &'a Expression<'a>) {
|
||||
use Expression::*;
|
||||
match self {
|
||||
VariableRef(x) => x.enforce_parents(expr),
|
||||
Constant(x) => x.enforce_parents(expr),
|
||||
Binary(x) => x.enforce_parents(expr),
|
||||
Unary(x) => x.enforce_parents(expr),
|
||||
Ternary(x) => x.enforce_parents(expr),
|
||||
Cast(x) => x.enforce_parents(expr),
|
||||
ArrayInline(x) => x.enforce_parents(expr),
|
||||
ArrayInit(x) => x.enforce_parents(expr),
|
||||
ArrayAccess(x) => x.enforce_parents(expr),
|
||||
ArrayRangeAccess(x) => x.enforce_parents(expr),
|
||||
TupleInit(x) => x.enforce_parents(expr),
|
||||
TupleAccess(x) => x.enforce_parents(expr),
|
||||
CircuitInit(x) => x.enforce_parents(expr),
|
||||
CircuitAccess(x) => x.enforce_parents(expr),
|
||||
Call(x) => x.enforce_parents(expr),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_type(&self) -> Option<Type<'a>> {
|
||||
use Expression::*;
|
||||
match self {
|
||||
VariableRef(x) => x.get_type(),
|
||||
Constant(x) => x.get_type(),
|
||||
Binary(x) => x.get_type(),
|
||||
Unary(x) => x.get_type(),
|
||||
Ternary(x) => x.get_type(),
|
||||
Cast(x) => x.get_type(),
|
||||
ArrayInline(x) => x.get_type(),
|
||||
ArrayInit(x) => x.get_type(),
|
||||
ArrayAccess(x) => x.get_type(),
|
||||
ArrayRangeAccess(x) => x.get_type(),
|
||||
TupleInit(x) => x.get_type(),
|
||||
TupleAccess(x) => x.get_type(),
|
||||
CircuitInit(x) => x.get_type(),
|
||||
CircuitAccess(x) => x.get_type(),
|
||||
Call(x) => x.get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_mut_ref(&self) -> bool {
|
||||
use Expression::*;
|
||||
match self {
|
||||
VariableRef(x) => x.is_mut_ref(),
|
||||
Constant(x) => x.is_mut_ref(),
|
||||
Binary(x) => x.is_mut_ref(),
|
||||
Unary(x) => x.is_mut_ref(),
|
||||
Ternary(x) => x.is_mut_ref(),
|
||||
Cast(x) => x.is_mut_ref(),
|
||||
ArrayInline(x) => x.is_mut_ref(),
|
||||
ArrayInit(x) => x.is_mut_ref(),
|
||||
ArrayAccess(x) => x.is_mut_ref(),
|
||||
ArrayRangeAccess(x) => x.is_mut_ref(),
|
||||
TupleInit(x) => x.is_mut_ref(),
|
||||
TupleAccess(x) => x.is_mut_ref(),
|
||||
CircuitInit(x) => x.is_mut_ref(),
|
||||
CircuitAccess(x) => x.is_mut_ref(),
|
||||
Call(x) => x.is_mut_ref(),
|
||||
}
|
||||
}
|
||||
|
||||
fn const_value(&self) -> Option<ConstValue> {
|
||||
use Expression::*;
|
||||
match self {
|
||||
VariableRef(x) => x.const_value(),
|
||||
Constant(x) => x.const_value(),
|
||||
Binary(x) => x.const_value(),
|
||||
Unary(x) => x.const_value(),
|
||||
Ternary(x) => x.const_value(),
|
||||
Cast(x) => x.const_value(),
|
||||
ArrayInline(x) => x.const_value(),
|
||||
ArrayInit(x) => x.const_value(),
|
||||
ArrayAccess(x) => x.const_value(),
|
||||
ArrayRangeAccess(x) => x.const_value(),
|
||||
TupleInit(x) => x.const_value(),
|
||||
TupleAccess(x) => x.const_value(),
|
||||
CircuitInit(x) => x.const_value(),
|
||||
CircuitAccess(x) => x.const_value(),
|
||||
Call(x) => x.const_value(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_consty(&self) -> bool {
|
||||
use Expression::*;
|
||||
match self {
|
||||
VariableRef(x) => x.is_consty(),
|
||||
Constant(x) => x.is_consty(),
|
||||
Binary(x) => x.is_consty(),
|
||||
Unary(x) => x.is_consty(),
|
||||
Ternary(x) => x.is_consty(),
|
||||
Cast(x) => x.is_consty(),
|
||||
ArrayInline(x) => x.is_consty(),
|
||||
ArrayInit(x) => x.is_consty(),
|
||||
ArrayAccess(x) => x.is_consty(),
|
||||
ArrayRangeAccess(x) => x.is_consty(),
|
||||
TupleInit(x) => x.is_consty(),
|
||||
TupleAccess(x) => x.is_consty(),
|
||||
CircuitInit(x) => x.is_consty(),
|
||||
CircuitAccess(x) => x.is_consty(),
|
||||
Call(x) => x.is_consty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::Expression> for &'a Expression<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &leo_ast::Expression,
|
||||
expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<Self, AsgConvertError> {
|
||||
use leo_ast::Expression::*;
|
||||
let expression = match value {
|
||||
Identifier(identifier) => Self::from_ast(scope, identifier, expected_type)?,
|
||||
Value(value) => scope
|
||||
.context
|
||||
.alloc_expression(Constant::from_ast(scope, value, expected_type).map(Expression::Constant)?),
|
||||
Binary(binary) => scope
|
||||
.context
|
||||
.alloc_expression(BinaryExpression::from_ast(scope, binary, expected_type).map(Expression::Binary)?),
|
||||
Unary(unary) => scope
|
||||
.context
|
||||
.alloc_expression(UnaryExpression::from_ast(scope, unary, expected_type).map(Expression::Unary)?),
|
||||
Ternary(conditional) => scope.context.alloc_expression(
|
||||
TernaryExpression::from_ast(scope, conditional, expected_type).map(Expression::Ternary)?,
|
||||
),
|
||||
Cast(cast) => scope
|
||||
.context
|
||||
.alloc_expression(CastExpression::from_ast(scope, cast, expected_type).map(Expression::Cast)?),
|
||||
|
||||
ArrayInline(array_inline) => scope.context.alloc_expression(
|
||||
ArrayInlineExpression::from_ast(scope, array_inline, expected_type).map(Expression::ArrayInline)?,
|
||||
),
|
||||
ArrayInit(array_init) => scope.context.alloc_expression(
|
||||
ArrayInitExpression::from_ast(scope, array_init, expected_type).map(Expression::ArrayInit)?,
|
||||
),
|
||||
ArrayAccess(array_access) => scope.context.alloc_expression(
|
||||
ArrayAccessExpression::from_ast(scope, array_access, expected_type).map(Expression::ArrayAccess)?,
|
||||
),
|
||||
ArrayRangeAccess(array_range_access) => scope.context.alloc_expression(
|
||||
ArrayRangeAccessExpression::from_ast(scope, array_range_access, expected_type)
|
||||
.map(Expression::ArrayRangeAccess)?,
|
||||
),
|
||||
|
||||
TupleInit(tuple_init) => scope.context.alloc_expression(
|
||||
TupleInitExpression::from_ast(scope, tuple_init, expected_type).map(Expression::TupleInit)?,
|
||||
),
|
||||
TupleAccess(tuple_access) => scope.context.alloc_expression(
|
||||
TupleAccessExpression::from_ast(scope, tuple_access, expected_type).map(Expression::TupleAccess)?,
|
||||
),
|
||||
|
||||
CircuitInit(circuit_init) => scope.context.alloc_expression(
|
||||
CircuitInitExpression::from_ast(scope, circuit_init, expected_type).map(Expression::CircuitInit)?,
|
||||
),
|
||||
CircuitMemberAccess(circuit_member) => scope.context.alloc_expression(
|
||||
CircuitAccessExpression::from_ast(scope, circuit_member, expected_type)
|
||||
.map(Expression::CircuitAccess)?,
|
||||
),
|
||||
CircuitStaticFunctionAccess(circuit_member) => scope.context.alloc_expression(
|
||||
CircuitAccessExpression::from_ast(scope, circuit_member, expected_type)
|
||||
.map(Expression::CircuitAccess)?,
|
||||
),
|
||||
|
||||
Call(call) => scope
|
||||
.context
|
||||
.alloc_expression(CallExpression::from_ast(scope, call, expected_type).map(Expression::Call)?),
|
||||
};
|
||||
expression.enforce_parents(&expression);
|
||||
Ok(expression)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::Expression> for &Expression<'a> {
|
||||
fn into(self) -> leo_ast::Expression {
|
||||
use Expression::*;
|
||||
match self {
|
||||
VariableRef(x) => leo_ast::Expression::Identifier(x.into()),
|
||||
Constant(x) => leo_ast::Expression::Value(x.into()),
|
||||
Binary(x) => leo_ast::Expression::Binary(x.into()),
|
||||
Unary(x) => leo_ast::Expression::Unary(x.into()),
|
||||
Ternary(x) => leo_ast::Expression::Ternary(x.into()),
|
||||
Cast(x) => leo_ast::Expression::Cast(x.into()),
|
||||
ArrayInline(x) => leo_ast::Expression::ArrayInline(x.into()),
|
||||
ArrayInit(x) => leo_ast::Expression::ArrayInit(x.into()),
|
||||
ArrayAccess(x) => leo_ast::Expression::ArrayAccess(x.into()),
|
||||
ArrayRangeAccess(x) => leo_ast::Expression::ArrayRangeAccess(x.into()),
|
||||
TupleInit(x) => leo_ast::Expression::TupleInit(x.into()),
|
||||
TupleAccess(x) => leo_ast::Expression::TupleAccess(x.into()),
|
||||
CircuitInit(x) => leo_ast::Expression::CircuitInit(x.into()),
|
||||
CircuitAccess(x) => x.into(),
|
||||
Call(x) => leo_ast::Expression::Call(x.into()),
|
||||
}
|
||||
}
|
||||
}
|
109
asg/src/expression/ternary.rs
Normal file
109
asg/src/expression/ternary.rs
Normal file
@ -0,0 +1,109 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{AsgConvertError, ConstValue, Expression, ExpressionNode, FromAst, Node, PartialType, Scope, Span, Type};
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TernaryExpression<'a> {
|
||||
pub parent: Cell<Option<&'a Expression<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub condition: Cell<&'a Expression<'a>>,
|
||||
pub if_true: Cell<&'a Expression<'a>>,
|
||||
pub if_false: Cell<&'a Expression<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Node for TernaryExpression<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExpressionNode<'a> for TernaryExpression<'a> {
|
||||
fn set_parent(&self, parent: &'a Expression<'a>) {
|
||||
self.parent.replace(Some(parent));
|
||||
}
|
||||
|
||||
fn get_parent(&self) -> Option<&'a Expression<'a>> {
|
||||
self.parent.get()
|
||||
}
|
||||
|
||||
fn enforce_parents(&self, expr: &'a Expression<'a>) {
|
||||
self.condition.get().set_parent(expr);
|
||||
self.if_true.get().set_parent(expr);
|
||||
self.if_false.get().set_parent(expr);
|
||||
}
|
||||
|
||||
fn get_type(&self) -> Option<Type<'a>> {
|
||||
self.if_true.get().get_type()
|
||||
}
|
||||
|
||||
fn is_mut_ref(&self) -> bool {
|
||||
self.if_true.get().is_mut_ref() && self.if_false.get().is_mut_ref()
|
||||
}
|
||||
|
||||
fn const_value(&self) -> Option<ConstValue> {
|
||||
if let Some(ConstValue::Boolean(switch)) = self.condition.get().const_value() {
|
||||
if switch {
|
||||
self.if_true.get().const_value()
|
||||
} else {
|
||||
self.if_false.get().const_value()
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn is_consty(&self) -> bool {
|
||||
self.condition.get().is_consty() && self.if_true.get().is_consty() && self.if_false.get().is_consty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::TernaryExpression> for TernaryExpression<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &leo_ast::TernaryExpression,
|
||||
expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<TernaryExpression<'a>, AsgConvertError> {
|
||||
Ok(TernaryExpression {
|
||||
parent: Cell::new(None),
|
||||
span: Some(value.span.clone()),
|
||||
condition: Cell::new(<&Expression<'a>>::from_ast(
|
||||
scope,
|
||||
&*value.condition,
|
||||
Some(Type::Boolean.partial()),
|
||||
)?),
|
||||
if_true: Cell::new(<&Expression<'a>>::from_ast(
|
||||
scope,
|
||||
&*value.if_true,
|
||||
expected_type.clone(),
|
||||
)?),
|
||||
if_false: Cell::new(<&Expression<'a>>::from_ast(scope, &*value.if_false, expected_type)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::TernaryExpression> for &TernaryExpression<'a> {
|
||||
fn into(self) -> leo_ast::TernaryExpression {
|
||||
leo_ast::TernaryExpression {
|
||||
condition: Box::new(self.condition.get().into()),
|
||||
if_true: Box::new(self.if_true.get().into()),
|
||||
if_false: Box::new(self.if_false.get().into()),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
117
asg/src/expression/tuple_access.rs
Normal file
117
asg/src/expression/tuple_access.rs
Normal file
@ -0,0 +1,117 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{AsgConvertError, ConstValue, Expression, ExpressionNode, FromAst, Node, PartialType, Scope, Span, Type};
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TupleAccessExpression<'a> {
|
||||
pub parent: Cell<Option<&'a Expression<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub tuple_ref: Cell<&'a Expression<'a>>,
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
impl<'a> Node for TupleAccessExpression<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExpressionNode<'a> for TupleAccessExpression<'a> {
|
||||
fn set_parent(&self, parent: &'a Expression<'a>) {
|
||||
self.parent.replace(Some(parent));
|
||||
}
|
||||
|
||||
fn get_parent(&self) -> Option<&'a Expression<'a>> {
|
||||
self.parent.get()
|
||||
}
|
||||
|
||||
fn enforce_parents(&self, expr: &'a Expression<'a>) {
|
||||
self.tuple_ref.get().set_parent(expr);
|
||||
}
|
||||
|
||||
fn get_type(&self) -> Option<Type<'a>> {
|
||||
match self.tuple_ref.get().get_type()? {
|
||||
Type::Tuple(subtypes) => subtypes.get(self.index).cloned(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_mut_ref(&self) -> bool {
|
||||
self.tuple_ref.get().is_mut_ref()
|
||||
}
|
||||
|
||||
fn const_value(&self) -> Option<ConstValue> {
|
||||
let tuple_const = self.tuple_ref.get().const_value()?;
|
||||
match tuple_const {
|
||||
ConstValue::Tuple(sub_consts) => sub_consts.get(self.index).cloned(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_consty(&self) -> bool {
|
||||
self.tuple_ref.get().is_consty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::TupleAccessExpression> for TupleAccessExpression<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &leo_ast::TupleAccessExpression,
|
||||
expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<TupleAccessExpression<'a>, AsgConvertError> {
|
||||
let index = value
|
||||
.index
|
||||
.value
|
||||
.parse::<usize>()
|
||||
.map_err(|_| AsgConvertError::parse_index_error())?;
|
||||
|
||||
let mut expected_tuple = vec![None; index + 1];
|
||||
expected_tuple[index] = expected_type;
|
||||
|
||||
let tuple = <&Expression<'a>>::from_ast(scope, &*value.tuple, Some(PartialType::Tuple(expected_tuple)))?;
|
||||
let tuple_type = tuple.get_type();
|
||||
if let Some(Type::Tuple(_items)) = tuple_type {
|
||||
} else {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
"a tuple",
|
||||
tuple_type.map(|x| x.to_string()).as_deref(),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(TupleAccessExpression {
|
||||
parent: Cell::new(None),
|
||||
span: Some(value.span.clone()),
|
||||
tuple_ref: Cell::new(tuple),
|
||||
index,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::TupleAccessExpression> for &TupleAccessExpression<'a> {
|
||||
fn into(self) -> leo_ast::TupleAccessExpression {
|
||||
leo_ast::TupleAccessExpression {
|
||||
tuple: Box::new(self.tuple_ref.get().into()),
|
||||
index: leo_ast::PositiveNumber {
|
||||
value: self.index.to_string().into(),
|
||||
},
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
135
asg/src/expression/tuple_init.rs
Normal file
135
asg/src/expression/tuple_init.rs
Normal file
@ -0,0 +1,135 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{AsgConvertError, ConstValue, Expression, ExpressionNode, FromAst, Node, PartialType, Scope, Span, Type};
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TupleInitExpression<'a> {
|
||||
pub parent: Cell<Option<&'a Expression<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub elements: Vec<Cell<&'a Expression<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a> Node for TupleInitExpression<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExpressionNode<'a> for TupleInitExpression<'a> {
|
||||
fn set_parent(&self, parent: &'a Expression<'a>) {
|
||||
self.parent.replace(Some(parent));
|
||||
}
|
||||
|
||||
fn get_parent(&self) -> Option<&'a Expression<'a>> {
|
||||
self.parent.get()
|
||||
}
|
||||
|
||||
fn enforce_parents(&self, expr: &'a Expression<'a>) {
|
||||
self.elements.iter().for_each(|element| {
|
||||
element.get().set_parent(expr);
|
||||
})
|
||||
}
|
||||
|
||||
fn get_type(&self) -> Option<Type<'a>> {
|
||||
let mut output = vec![];
|
||||
for element in self.elements.iter() {
|
||||
output.push(element.get().get_type()?);
|
||||
}
|
||||
Some(Type::Tuple(output))
|
||||
}
|
||||
|
||||
fn is_mut_ref(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn const_value(&self) -> Option<ConstValue> {
|
||||
let mut consts = vec![];
|
||||
for element in self.elements.iter() {
|
||||
if let Some(const_value) = element.get().const_value() {
|
||||
consts.push(const_value);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(ConstValue::Tuple(consts))
|
||||
}
|
||||
|
||||
fn is_consty(&self) -> bool {
|
||||
self.elements.iter().all(|x| x.get().is_consty())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::TupleInitExpression> for TupleInitExpression<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &leo_ast::TupleInitExpression,
|
||||
expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<TupleInitExpression<'a>, AsgConvertError> {
|
||||
let tuple_types = match expected_type {
|
||||
Some(PartialType::Tuple(sub_types)) => Some(sub_types),
|
||||
None => None,
|
||||
x => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
"tuple",
|
||||
x.map(|x| x.to_string()).as_deref(),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(tuple_types) = tuple_types.as_ref() {
|
||||
if tuple_types.len() != value.elements.len() {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&*format!("tuple of length {}", tuple_types.len()),
|
||||
Some(&*format!("tuple of length {}", value.elements.len())),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let elements = value
|
||||
.elements
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, e)| {
|
||||
<&Expression<'a>>::from_ast(
|
||||
scope,
|
||||
e,
|
||||
tuple_types.as_ref().map(|x| x.get(i)).flatten().cloned().flatten(),
|
||||
)
|
||||
.map(Cell::new)
|
||||
})
|
||||
.collect::<Result<Vec<_>, AsgConvertError>>()?;
|
||||
|
||||
Ok(TupleInitExpression {
|
||||
parent: Cell::new(None),
|
||||
span: Some(value.span.clone()),
|
||||
elements,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::TupleInitExpression> for &TupleInitExpression<'a> {
|
||||
fn into(self) -> leo_ast::TupleInitExpression {
|
||||
leo_ast::TupleInitExpression {
|
||||
elements: self.elements.iter().map(|e| e.get().into()).collect(),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
160
asg/src/expression/unary.rs
Normal file
160
asg/src/expression/unary.rs
Normal file
@ -0,0 +1,160 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{AsgConvertError, ConstValue, Expression, ExpressionNode, FromAst, Node, PartialType, Scope, Span, Type};
|
||||
pub use leo_ast::UnaryOperation;
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UnaryExpression<'a> {
|
||||
pub parent: Cell<Option<&'a Expression<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub operation: UnaryOperation,
|
||||
pub inner: Cell<&'a Expression<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Node for UnaryExpression<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExpressionNode<'a> for UnaryExpression<'a> {
|
||||
fn set_parent(&self, parent: &'a Expression<'a>) {
|
||||
self.parent.replace(Some(parent));
|
||||
}
|
||||
|
||||
fn get_parent(&self) -> Option<&'a Expression<'a>> {
|
||||
self.parent.get()
|
||||
}
|
||||
|
||||
fn enforce_parents(&self, expr: &'a Expression<'a>) {
|
||||
self.inner.get().set_parent(expr);
|
||||
}
|
||||
|
||||
fn get_type(&self) -> Option<Type<'a>> {
|
||||
self.inner.get().get_type()
|
||||
}
|
||||
|
||||
fn is_mut_ref(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn const_value(&self) -> Option<ConstValue> {
|
||||
if let Some(inner) = self.inner.get().const_value() {
|
||||
match self.operation {
|
||||
UnaryOperation::Not => match inner {
|
||||
ConstValue::Boolean(value) => Some(ConstValue::Boolean(!value)),
|
||||
_ => None,
|
||||
},
|
||||
UnaryOperation::Negate => {
|
||||
match inner {
|
||||
ConstValue::Int(value) => Some(ConstValue::Int(value.value_negate()?)),
|
||||
// ConstValue::Group(value) => Some(ConstValue::Group(value)), TODO: groups
|
||||
// ConstValue::Field(value) => Some(ConstValue::Field(-value)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
UnaryOperation::BitNot => match inner {
|
||||
ConstValue::Int(value) => Some(ConstValue::Int(value.value_bit_negate()?)),
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn is_consty(&self) -> bool {
|
||||
self.inner.get().is_consty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::UnaryExpression> for UnaryExpression<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &leo_ast::UnaryExpression,
|
||||
expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<UnaryExpression<'a>, AsgConvertError> {
|
||||
let expected_type = match value.op {
|
||||
UnaryOperation::Not => match expected_type.map(|x| x.full()).flatten() {
|
||||
Some(Type::Boolean) | None => Some(Type::Boolean),
|
||||
Some(type_) => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&type_.to_string(),
|
||||
Some(&*Type::Boolean.to_string()),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
},
|
||||
UnaryOperation::Negate => match expected_type.map(|x| x.full()).flatten() {
|
||||
Some(type_ @ Type::Integer(_)) => Some(type_),
|
||||
Some(Type::Group) => Some(Type::Group),
|
||||
Some(Type::Field) => Some(Type::Field),
|
||||
None => None,
|
||||
Some(type_) => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&type_.to_string(),
|
||||
Some("integer, group, field"),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
},
|
||||
UnaryOperation::BitNot => match expected_type.map(|x| x.full()).flatten() {
|
||||
Some(type_ @ Type::Integer(_)) => Some(type_),
|
||||
None => None,
|
||||
Some(type_) => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&type_.to_string(),
|
||||
Some("integer"),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
},
|
||||
};
|
||||
let expr = <&Expression<'a>>::from_ast(scope, &*value.inner, expected_type.map(Into::into))?;
|
||||
|
||||
if matches!(value.op, UnaryOperation::Negate) {
|
||||
let is_expr_unsigned = expr
|
||||
.get_type()
|
||||
.map(|x| match x {
|
||||
Type::Integer(x) => !x.is_signed(),
|
||||
_ => false,
|
||||
})
|
||||
.unwrap_or(false);
|
||||
if is_expr_unsigned {
|
||||
return Err(AsgConvertError::unsigned_negation(&value.span));
|
||||
}
|
||||
}
|
||||
Ok(UnaryExpression {
|
||||
parent: Cell::new(None),
|
||||
span: Some(value.span.clone()),
|
||||
operation: value.op.clone(),
|
||||
inner: Cell::new(expr),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::UnaryExpression> for &UnaryExpression<'a> {
|
||||
fn into(self) -> leo_ast::UnaryExpression {
|
||||
leo_ast::UnaryExpression {
|
||||
op: self.operation.clone(),
|
||||
inner: Box::new(self.inner.get().into()),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
194
asg/src/expression/variable_ref.rs
Normal file
194
asg/src/expression/variable_ref.rs
Normal file
@ -0,0 +1,194 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
AsgConvertError,
|
||||
ConstValue,
|
||||
Constant,
|
||||
DefinitionStatement,
|
||||
Expression,
|
||||
ExpressionNode,
|
||||
FromAst,
|
||||
Node,
|
||||
PartialType,
|
||||
Scope,
|
||||
Span,
|
||||
Statement,
|
||||
Type,
|
||||
Variable,
|
||||
};
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VariableRef<'a> {
|
||||
pub parent: Cell<Option<&'a Expression<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub variable: &'a Variable<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Node for VariableRef<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ExpressionNode<'a> for VariableRef<'a> {
|
||||
fn set_parent(&self, parent: &'a Expression<'a>) {
|
||||
self.parent.replace(Some(parent));
|
||||
}
|
||||
|
||||
fn get_parent(&self) -> Option<&'a Expression<'a>> {
|
||||
self.parent.get()
|
||||
}
|
||||
|
||||
fn enforce_parents(&self, _expr: &'a Expression<'a>) {}
|
||||
|
||||
fn get_type(&self) -> Option<Type<'a>> {
|
||||
Some(self.variable.borrow().type_.clone())
|
||||
}
|
||||
|
||||
fn is_mut_ref(&self) -> bool {
|
||||
self.variable.borrow().mutable
|
||||
}
|
||||
|
||||
// todo: we can use use hacky ssa here to catch more cases, or just enforce ssa before asg generation finished
|
||||
fn const_value(&self) -> Option<ConstValue> {
|
||||
let variable = self.variable.borrow();
|
||||
if variable.mutable || variable.assignments.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
let assignment = variable.assignments.get(0).unwrap();
|
||||
match &*assignment {
|
||||
Statement::Definition(DefinitionStatement { variables, value, .. }) => {
|
||||
if variables.len() == 1 {
|
||||
let defined_variable = variables.get(0).unwrap().borrow();
|
||||
assert_eq!(variable.id, defined_variable.id);
|
||||
|
||||
value.get().const_value()
|
||||
} else {
|
||||
for (i, defined_variable) in variables.iter().enumerate() {
|
||||
let defined_variable = defined_variable.borrow();
|
||||
if defined_variable.id == variable.id {
|
||||
match value.get().const_value() {
|
||||
Some(ConstValue::Tuple(values)) => return values.get(i).cloned(),
|
||||
None => return None,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
panic!("no corresponding tuple variable found during const destructuring (corrupt asg?)");
|
||||
}
|
||||
}
|
||||
_ => None, //todo unroll loops during asg phase
|
||||
}
|
||||
}
|
||||
|
||||
fn is_consty(&self) -> bool {
|
||||
let variable = self.variable.borrow();
|
||||
if variable.const_ {
|
||||
return true;
|
||||
}
|
||||
if variable.mutable || variable.assignments.len() != 1 {
|
||||
return false;
|
||||
}
|
||||
let assignment = variable.assignments.get(0).unwrap();
|
||||
|
||||
match &*assignment {
|
||||
Statement::Definition(DefinitionStatement { variables, value, .. }) => {
|
||||
if variables.len() == 1 {
|
||||
let defined_variable = variables.get(0).unwrap().borrow();
|
||||
assert_eq!(variable.id, defined_variable.id);
|
||||
|
||||
value.get().is_consty()
|
||||
} else {
|
||||
for defined_variable in variables.iter() {
|
||||
let defined_variable = defined_variable.borrow();
|
||||
if defined_variable.id == variable.id {
|
||||
return value.get().is_consty();
|
||||
}
|
||||
}
|
||||
panic!("no corresponding tuple variable found during const destructuring (corrupt asg?)");
|
||||
}
|
||||
}
|
||||
Statement::Iteration(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::Identifier> for &'a Expression<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &leo_ast::Identifier,
|
||||
expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<&'a Expression<'a>, AsgConvertError> {
|
||||
let variable = if value.name.as_ref() == "input" {
|
||||
if let Some(input) = scope.resolve_input() {
|
||||
input.container
|
||||
} else {
|
||||
return Err(AsgConvertError::InternalError(
|
||||
"attempted to reference input when none is in scope".to_string(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
match scope.resolve_variable(&value.name) {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
if value.name.starts_with("aleo1") {
|
||||
return Ok(scope.context.alloc_expression(Expression::Constant(Constant {
|
||||
parent: Cell::new(None),
|
||||
span: Some(value.span.clone()),
|
||||
value: ConstValue::Address(value.name.clone()),
|
||||
})));
|
||||
}
|
||||
return Err(AsgConvertError::unresolved_reference(&value.name, &value.span));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let variable_ref = VariableRef {
|
||||
parent: Cell::new(None),
|
||||
span: Some(value.span.clone()),
|
||||
variable,
|
||||
};
|
||||
let expression = scope.context.alloc_expression(Expression::VariableRef(variable_ref));
|
||||
|
||||
if let Some(expected_type) = expected_type {
|
||||
let type_ = expression
|
||||
.get_type()
|
||||
.ok_or_else(|| AsgConvertError::unresolved_reference(&value.name, &value.span))?;
|
||||
if !expected_type.matches(&type_) {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&expected_type.to_string(),
|
||||
Some(&*type_.to_string()),
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let mut variable_ref = variable.borrow_mut();
|
||||
variable_ref.references.push(expression);
|
||||
|
||||
Ok(expression)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::Identifier> for &VariableRef<'a> {
|
||||
fn into(self) -> leo_ast::Identifier {
|
||||
self.variable.borrow().name.clone()
|
||||
}
|
||||
}
|
89
asg/src/import.rs
Normal file
89
asg/src/import.rs
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Helper methods for resolving imported packages.
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::{AsgContext, AsgConvertError, Program, Span};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
|
||||
pub trait ImportResolver<'a> {
|
||||
fn resolve_package(
|
||||
&mut self,
|
||||
context: AsgContext<'a>,
|
||||
package_segments: &[&str],
|
||||
span: &Span,
|
||||
) -> Result<Option<Program<'a>>, AsgConvertError>;
|
||||
}
|
||||
|
||||
pub struct NullImportResolver;
|
||||
|
||||
impl<'a> ImportResolver<'a> for NullImportResolver {
|
||||
fn resolve_package(
|
||||
&mut self,
|
||||
_context: AsgContext<'a>,
|
||||
_package_segments: &[&str],
|
||||
_span: &Span,
|
||||
) -> Result<Option<Program<'a>>, AsgConvertError> {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CoreImportResolver<'a, 'b, T: ImportResolver<'b>> {
|
||||
inner: &'a mut T,
|
||||
lifetime: PhantomData<&'b ()>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, T: ImportResolver<'b>> CoreImportResolver<'a, 'b, T> {
|
||||
pub fn new(inner: &'a mut T) -> Self {
|
||||
CoreImportResolver {
|
||||
inner,
|
||||
lifetime: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, T: ImportResolver<'b>> ImportResolver<'b> for CoreImportResolver<'a, 'b, T> {
|
||||
fn resolve_package(
|
||||
&mut self,
|
||||
context: AsgContext<'b>,
|
||||
package_segments: &[&str],
|
||||
span: &Span,
|
||||
) -> Result<Option<Program<'b>>, AsgConvertError> {
|
||||
if !package_segments.is_empty() && package_segments.get(0).unwrap() == &"core" {
|
||||
Ok(crate::resolve_core_module(context, &*package_segments[1..].join("."))?)
|
||||
} else {
|
||||
self.inner.resolve_package(context, package_segments, span)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MockedImportResolver<'a> {
|
||||
pub packages: IndexMap<String, Program<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> ImportResolver<'a> for MockedImportResolver<'a> {
|
||||
fn resolve_package(
|
||||
&mut self,
|
||||
_context: AsgContext<'a>,
|
||||
package_segments: &[&str],
|
||||
_span: &Span,
|
||||
) -> Result<Option<Program<'a>>, AsgConvertError> {
|
||||
Ok(self.packages.get(&package_segments.join(".")).cloned())
|
||||
}
|
||||
}
|
106
asg/src/input.rs
Normal file
106
asg/src/input.rs
Normal file
@ -0,0 +1,106 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{Circuit, CircuitMember, Identifier, Scope, Type, Variable};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use std::cell::RefCell;
|
||||
|
||||
/// Stores program input values as ASG nodes.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Input<'a> {
|
||||
pub registers: &'a Circuit<'a>,
|
||||
pub state: &'a Circuit<'a>,
|
||||
pub state_leaf: &'a Circuit<'a>,
|
||||
pub record: &'a Circuit<'a>,
|
||||
pub container_circuit: &'a Circuit<'a>,
|
||||
pub container: &'a Variable<'a>,
|
||||
}
|
||||
|
||||
pub const CONTAINER_PSEUDO_CIRCUIT: &str = "$InputContainer";
|
||||
pub const REGISTERS_PSEUDO_CIRCUIT: &str = "$InputRegister";
|
||||
pub const RECORD_PSEUDO_CIRCUIT: &str = "$InputRecord";
|
||||
pub const STATE_PSEUDO_CIRCUIT: &str = "$InputState";
|
||||
pub const STATE_LEAF_PSEUDO_CIRCUIT: &str = "$InputStateLeaf";
|
||||
|
||||
impl<'a> Input<'a> {
|
||||
fn make_header(scope: &'a Scope<'a>, name: &str) -> &'a Circuit<'a> {
|
||||
scope.context.alloc_circuit(Circuit {
|
||||
id: scope.context.get_id(),
|
||||
name: RefCell::new(Identifier::new(name.into())),
|
||||
members: RefCell::new(IndexMap::new()),
|
||||
core_mapping: RefCell::new(None),
|
||||
scope,
|
||||
span: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new(scope: &'a Scope<'a>) -> Self {
|
||||
let input_scope = scope.make_subscope();
|
||||
let registers = Self::make_header(input_scope, REGISTERS_PSEUDO_CIRCUIT);
|
||||
let record = Self::make_header(input_scope, RECORD_PSEUDO_CIRCUIT);
|
||||
let state = Self::make_header(input_scope, STATE_PSEUDO_CIRCUIT);
|
||||
let state_leaf = Self::make_header(input_scope, STATE_LEAF_PSEUDO_CIRCUIT);
|
||||
|
||||
let mut container_members = IndexMap::new();
|
||||
container_members.insert(
|
||||
"registers".to_string(),
|
||||
CircuitMember::Variable(Type::Circuit(registers)),
|
||||
);
|
||||
container_members.insert("record".to_string(), CircuitMember::Variable(Type::Circuit(record)));
|
||||
container_members.insert("state".to_string(), CircuitMember::Variable(Type::Circuit(state)));
|
||||
container_members.insert(
|
||||
"state_leaf".to_string(),
|
||||
CircuitMember::Variable(Type::Circuit(state_leaf)),
|
||||
);
|
||||
|
||||
let container_circuit = input_scope.context.alloc_circuit(Circuit {
|
||||
id: scope.context.get_id(),
|
||||
name: RefCell::new(Identifier::new(CONTAINER_PSEUDO_CIRCUIT.into())),
|
||||
members: RefCell::new(container_members),
|
||||
core_mapping: RefCell::new(None),
|
||||
scope: input_scope,
|
||||
span: Default::default(),
|
||||
});
|
||||
|
||||
Input {
|
||||
registers,
|
||||
record,
|
||||
state,
|
||||
state_leaf,
|
||||
container_circuit,
|
||||
container: input_scope.context.alloc_variable(RefCell::new(crate::InnerVariable {
|
||||
id: scope.context.get_id(),
|
||||
name: Identifier::new("input".into()),
|
||||
type_: Type::Circuit(container_circuit),
|
||||
mutable: false,
|
||||
const_: false,
|
||||
declaration: crate::VariableDeclaration::Input,
|
||||
references: vec![],
|
||||
assignments: vec![],
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Circuit<'a> {
|
||||
pub fn is_input_pseudo_circuit(&self) -> bool {
|
||||
matches!(
|
||||
&*self.name.borrow().name,
|
||||
REGISTERS_PSEUDO_CIRCUIT | RECORD_PSEUDO_CIRCUIT | STATE_PSEUDO_CIRCUIT | STATE_LEAF_PSEUDO_CIRCUIT
|
||||
)
|
||||
}
|
||||
}
|
146
asg/src/lib.rs
Normal file
146
asg/src/lib.rs
Normal file
@ -0,0 +1,146 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! The abstract semantic graph (ASG) for a Leo program.
|
||||
//!
|
||||
//! This module contains the [`Asg`] type, an abstract data type that represents a Leo program
|
||||
//! as a series of graph nodes. The [`Asg`] type is at a greater level of abstraction than an [`Ast`].
|
||||
//!
|
||||
//! A new [`Asg`] type can be created from an [`Ast`].
|
||||
//! Converting to an [`Asg`] provides greater type safety by canonicalizing and checking program types.
|
||||
|
||||
#![allow(clippy::from_over_into)]
|
||||
#![allow(clippy::result_unit_err)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate thiserror;
|
||||
|
||||
pub mod checks;
|
||||
pub use checks::*;
|
||||
|
||||
pub mod const_value;
|
||||
pub use const_value::*;
|
||||
|
||||
pub mod error;
|
||||
pub use error::*;
|
||||
|
||||
pub mod expression;
|
||||
pub use expression::*;
|
||||
|
||||
pub mod import;
|
||||
pub use import::*;
|
||||
|
||||
mod input;
|
||||
pub use input::*;
|
||||
|
||||
pub mod node;
|
||||
pub use node::*;
|
||||
|
||||
pub mod prelude;
|
||||
pub use prelude::*;
|
||||
|
||||
pub mod program;
|
||||
pub use program::*;
|
||||
|
||||
pub mod reducer;
|
||||
pub use reducer::*;
|
||||
|
||||
pub mod scope;
|
||||
pub use scope::*;
|
||||
|
||||
pub mod statement;
|
||||
pub use statement::*;
|
||||
|
||||
pub mod type_;
|
||||
pub use type_::*;
|
||||
|
||||
pub mod variable;
|
||||
use typed_arena::Arena;
|
||||
pub use variable::*;
|
||||
|
||||
pub mod pass;
|
||||
pub use pass::*;
|
||||
|
||||
pub mod context;
|
||||
pub use context::*;
|
||||
|
||||
pub use leo_ast::{Ast, Identifier, Span};
|
||||
|
||||
/// The abstract semantic graph (ASG) for a Leo program.
|
||||
///
|
||||
/// The [`Asg`] type represents a Leo program as a series of recursive data types.
|
||||
/// These data types form a graph that begins from a [`Program`] type node.
|
||||
///
|
||||
/// A new [`Asg`] can be created from an [`Ast`] generated in the `ast` module.
|
||||
#[derive(Clone)]
|
||||
pub struct Asg<'a> {
|
||||
context: AsgContext<'a>,
|
||||
asg: Program<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Asg<'a> {
|
||||
/// Creates a new ASG from a given AST and import resolver.
|
||||
pub fn new<T: ImportResolver<'a>, Y: AsRef<leo_ast::Program>>(
|
||||
context: AsgContext<'a>,
|
||||
ast: Y,
|
||||
resolver: &mut T,
|
||||
) -> Result<Self, AsgConvertError> {
|
||||
Ok(Self {
|
||||
context,
|
||||
asg: Program::new(context, ast.as_ref(), resolver)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the internal program ASG representation.
|
||||
pub fn as_repr(&self) -> &Program<'a> {
|
||||
&self.asg
|
||||
}
|
||||
|
||||
pub fn into_repr(self) -> Program<'a> {
|
||||
self.asg
|
||||
}
|
||||
|
||||
// /// Serializes the ast into a JSON string.
|
||||
// pub fn to_json_string(&self) -> Result<String, serde_json::Error> {
|
||||
// serde_json::to_string_pretty(&self.asg)
|
||||
// }
|
||||
//
|
||||
// /// Deserializes the JSON string into a ast.
|
||||
// pub fn from_json_string(json: &str) -> Result<Self, serde_json::Error> {
|
||||
// let ast: Program = serde_json::from_str(json)?;
|
||||
// Ok(Self { ast })
|
||||
// }
|
||||
}
|
||||
|
||||
// TODO (howardwu): Remove this.
|
||||
pub fn load_asg<'a, T: ImportResolver<'a>>(
|
||||
context: AsgContext<'a>,
|
||||
content: &str,
|
||||
resolver: &mut T,
|
||||
) -> Result<Program<'a>, AsgConvertError> {
|
||||
// Parses the Leo file and constructs a grammar ast.
|
||||
let ast = leo_parser::parse_ast("input.leo", content)?;
|
||||
|
||||
Program::new(context, ast.as_repr(), resolver)
|
||||
}
|
||||
|
||||
pub fn new_alloc_context<'a>() -> Arena<ArenaNode<'a>> {
|
||||
Arena::new()
|
||||
}
|
||||
|
||||
pub fn new_context<'a>(arena: &'a Arena<ArenaNode<'a>>) -> AsgContext<'a> {
|
||||
AsgContextInner::new(arena)
|
||||
}
|
53
asg/src/node.rs
Normal file
53
asg/src/node.rs
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
AsgContextInner,
|
||||
AsgConvertError,
|
||||
Circuit,
|
||||
Expression,
|
||||
Function,
|
||||
PartialType,
|
||||
Scope,
|
||||
Span,
|
||||
Statement,
|
||||
Variable,
|
||||
};
|
||||
|
||||
/// A node in the abstract semantic graph.
|
||||
pub trait Node {
|
||||
fn span(&self) -> Option<&Span>;
|
||||
}
|
||||
|
||||
pub(super) trait FromAst<'a, T: leo_ast::Node + 'static>: Sized {
|
||||
// expected_type contract: if present, output expression must be of type expected_type.
|
||||
// type of an element may NEVER be None unless it is functionally a non-expression. (static call targets, function ref call targets are not expressions)
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &T,
|
||||
expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<Self, AsgConvertError>;
|
||||
}
|
||||
|
||||
pub enum ArenaNode<'a> {
|
||||
Expression(Expression<'a>),
|
||||
Scope(Box<Scope<'a>>),
|
||||
Statement(Statement<'a>),
|
||||
Variable(Variable<'a>),
|
||||
Circuit(Circuit<'a>),
|
||||
Function(Function<'a>),
|
||||
Inner(AsgContextInner<'a>),
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2020 Aleo Systems Inc.
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
@ -14,14 +14,9 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#[macro_use]
|
||||
extern crate thiserror;
|
||||
use crate::Program;
|
||||
pub use leo_ast::FormattedError;
|
||||
|
||||
pub mod arithmetic;
|
||||
|
||||
pub mod bits;
|
||||
|
||||
pub mod errors;
|
||||
|
||||
pub mod signed_integer;
|
||||
pub use self::signed_integer::*;
|
||||
pub trait AsgPass<'a> {
|
||||
fn do_pass(asg: Program<'a>) -> Result<Program<'a>, FormattedError>;
|
||||
}
|
43
asg/src/prelude.rs
Normal file
43
asg/src/prelude.rs
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
// TODO (protryon): We should merge this with core
|
||||
|
||||
use crate::{AsgContext, AsgConvertError, Program};
|
||||
|
||||
// TODO (protryon): Make asg deep copy so we can cache resolved core modules
|
||||
// TODO (protryon): Figure out how to do headers without bogus returns
|
||||
|
||||
pub fn resolve_core_module<'a>(context: AsgContext<'a>, module: &str) -> Result<Option<Program<'a>>, AsgConvertError> {
|
||||
match module {
|
||||
"unstable.blake2s" => {
|
||||
let asg = crate::load_asg(
|
||||
context,
|
||||
r#"
|
||||
circuit Blake2s {
|
||||
function hash(seed: [u8; 32], message: [u8; 32]) -> [u8; 32] {
|
||||
return [0; 32];
|
||||
}
|
||||
}
|
||||
"#,
|
||||
&mut crate::NullImportResolver,
|
||||
)?;
|
||||
asg.set_core_mapping("blake2s");
|
||||
Ok(Some(asg))
|
||||
}
|
||||
_ => Ok(None),
|
||||
}
|
||||
}
|
164
asg/src/program/circuit.rs
Normal file
164
asg/src/program/circuit.rs
Normal file
@ -0,0 +1,164 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{AsgConvertError, Function, Identifier, Node, Scope, Span, Type};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use std::cell::RefCell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum CircuitMember<'a> {
|
||||
Variable(Type<'a>),
|
||||
Function(&'a Function<'a>),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Circuit<'a> {
|
||||
pub id: u32,
|
||||
pub name: RefCell<Identifier>,
|
||||
pub core_mapping: RefCell<Option<String>>,
|
||||
pub scope: &'a Scope<'a>,
|
||||
pub span: Option<Span>,
|
||||
pub members: RefCell<IndexMap<String, CircuitMember<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a> PartialEq for Circuit<'a> {
|
||||
fn eq(&self, other: &Circuit) -> bool {
|
||||
if self.name != other.name {
|
||||
return false;
|
||||
}
|
||||
self.id == other.id
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Eq for Circuit<'a> {}
|
||||
|
||||
impl<'a> Node for Circuit<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Circuit<'a> {
|
||||
pub(super) fn init(scope: &'a Scope<'a>, value: &leo_ast::Circuit) -> Result<&'a Circuit<'a>, AsgConvertError> {
|
||||
let new_scope = scope.make_subscope();
|
||||
|
||||
let circuit = scope.context.alloc_circuit(Circuit {
|
||||
id: scope.context.get_id(),
|
||||
name: RefCell::new(value.circuit_name.clone()),
|
||||
members: RefCell::new(IndexMap::new()),
|
||||
core_mapping: RefCell::new(None),
|
||||
span: Some(value.circuit_name.span.clone()),
|
||||
scope: new_scope,
|
||||
});
|
||||
new_scope.circuit_self.replace(Some(circuit));
|
||||
|
||||
let mut members = circuit.members.borrow_mut();
|
||||
for member in value.members.iter() {
|
||||
if let leo_ast::CircuitMember::CircuitVariable(name, type_) = member {
|
||||
if members.contains_key(name.name.as_ref()) {
|
||||
return Err(AsgConvertError::redefined_circuit_member(
|
||||
&value.circuit_name.name,
|
||||
&name.name,
|
||||
&name.span,
|
||||
));
|
||||
}
|
||||
members.insert(
|
||||
name.name.to_string(),
|
||||
CircuitMember::Variable(new_scope.resolve_ast_type(type_)?),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(circuit)
|
||||
}
|
||||
|
||||
pub(super) fn init_member(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &leo_ast::Circuit,
|
||||
) -> Result<&'a Circuit<'a>, AsgConvertError> {
|
||||
let new_scope = scope.make_subscope();
|
||||
let circuits = scope.circuits.borrow();
|
||||
|
||||
let circuit = circuits.get(value.circuit_name.name.as_ref()).unwrap();
|
||||
new_scope.circuit_self.replace(Some(circuit));
|
||||
|
||||
let mut members = circuit.members.borrow_mut();
|
||||
for member in value.members.iter() {
|
||||
if let leo_ast::CircuitMember::CircuitFunction(function) = member {
|
||||
if members.contains_key(function.identifier.name.as_ref()) {
|
||||
return Err(AsgConvertError::redefined_circuit_member(
|
||||
&value.circuit_name.name,
|
||||
&function.identifier.name,
|
||||
&function.identifier.span,
|
||||
));
|
||||
}
|
||||
let asg_function = Function::init(new_scope, function)?;
|
||||
asg_function.circuit.replace(Some(circuit));
|
||||
if asg_function.is_test() {
|
||||
return Err(AsgConvertError::circuit_test_function(&function.identifier.span));
|
||||
}
|
||||
members.insert(
|
||||
function.identifier.name.to_string(),
|
||||
CircuitMember::Function(asg_function),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(circuit)
|
||||
}
|
||||
|
||||
pub(super) fn fill_from_ast(self: &'a Circuit<'a>, value: &leo_ast::Circuit) -> Result<(), AsgConvertError> {
|
||||
for member in value.members.iter() {
|
||||
match member {
|
||||
leo_ast::CircuitMember::CircuitVariable(..) => {}
|
||||
leo_ast::CircuitMember::CircuitFunction(function) => {
|
||||
let asg_function = match *self
|
||||
.members
|
||||
.borrow()
|
||||
.get(function.identifier.name.as_ref())
|
||||
.expect("missing header for defined circuit function")
|
||||
{
|
||||
CircuitMember::Function(f) => f,
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
Function::fill_from_ast(asg_function, function)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::Circuit> for &Circuit<'a> {
|
||||
fn into(self) -> leo_ast::Circuit {
|
||||
let members = self
|
||||
.members
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|(name, member)| match &member {
|
||||
CircuitMember::Variable(type_) => {
|
||||
leo_ast::CircuitMember::CircuitVariable(Identifier::new((&**name).into()), type_.into())
|
||||
}
|
||||
CircuitMember::Function(func) => leo_ast::CircuitMember::CircuitFunction((*func).into()),
|
||||
})
|
||||
.collect();
|
||||
leo_ast::Circuit {
|
||||
circuit_name: self.name.borrow().clone(),
|
||||
members,
|
||||
}
|
||||
}
|
||||
}
|
224
asg/src/program/function.rs
Normal file
224
asg/src/program/function.rs
Normal file
@ -0,0 +1,224 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
AsgConvertError,
|
||||
BlockStatement,
|
||||
Circuit,
|
||||
FromAst,
|
||||
Identifier,
|
||||
MonoidalDirector,
|
||||
ReturnPathReducer,
|
||||
Scope,
|
||||
Span,
|
||||
Statement,
|
||||
Type,
|
||||
Variable,
|
||||
};
|
||||
use indexmap::IndexMap;
|
||||
pub use leo_ast::Annotation;
|
||||
use leo_ast::FunctionInput;
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum FunctionQualifier {
|
||||
SelfRef,
|
||||
ConstSelfRef,
|
||||
MutSelfRef,
|
||||
Static,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Function<'a> {
|
||||
pub id: u32,
|
||||
pub name: RefCell<Identifier>,
|
||||
pub output: Type<'a>,
|
||||
pub arguments: IndexMap<String, Cell<&'a Variable<'a>>>,
|
||||
pub circuit: Cell<Option<&'a Circuit<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub body: Cell<Option<&'a Statement<'a>>>,
|
||||
pub scope: &'a Scope<'a>,
|
||||
pub qualifier: FunctionQualifier,
|
||||
pub annotations: Vec<Annotation>,
|
||||
}
|
||||
|
||||
impl<'a> PartialEq for Function<'a> {
|
||||
fn eq(&self, other: &Function<'a>) -> bool {
|
||||
if self.name.borrow().name != other.name.borrow().name {
|
||||
return false;
|
||||
}
|
||||
self.id == other.id
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Eq for Function<'a> {}
|
||||
|
||||
impl<'a> Function<'a> {
|
||||
pub(crate) fn init(scope: &'a Scope<'a>, value: &leo_ast::Function) -> Result<&'a Function<'a>, AsgConvertError> {
|
||||
let output: Type<'a> = value
|
||||
.output
|
||||
.as_ref()
|
||||
.map(|t| scope.resolve_ast_type(t))
|
||||
.transpose()?
|
||||
.unwrap_or_else(|| Type::Tuple(vec![]));
|
||||
let mut qualifier = FunctionQualifier::Static;
|
||||
let new_scope = scope.make_subscope();
|
||||
|
||||
let mut arguments = IndexMap::new();
|
||||
{
|
||||
for input in value.input.iter() {
|
||||
match input {
|
||||
FunctionInput::SelfKeyword(_) => {
|
||||
qualifier = FunctionQualifier::SelfRef;
|
||||
}
|
||||
FunctionInput::ConstSelfKeyword(_) => {
|
||||
qualifier = FunctionQualifier::ConstSelfRef;
|
||||
}
|
||||
FunctionInput::MutSelfKeyword(_) => {
|
||||
qualifier = FunctionQualifier::MutSelfRef;
|
||||
}
|
||||
FunctionInput::Variable(leo_ast::FunctionInputVariable {
|
||||
type_,
|
||||
identifier,
|
||||
const_,
|
||||
mutable,
|
||||
..
|
||||
}) => {
|
||||
let variable = scope.context.alloc_variable(RefCell::new(crate::InnerVariable {
|
||||
id: scope.context.get_id(),
|
||||
name: identifier.clone(),
|
||||
type_: scope.resolve_ast_type(&type_)?,
|
||||
mutable: *mutable,
|
||||
const_: *const_,
|
||||
declaration: crate::VariableDeclaration::Parameter,
|
||||
references: vec![],
|
||||
assignments: vec![],
|
||||
}));
|
||||
arguments.insert(identifier.name.to_string(), Cell::new(&*variable));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if qualifier != FunctionQualifier::Static && scope.circuit_self.get().is_none() {
|
||||
return Err(AsgConvertError::invalid_self_in_global(&value.span));
|
||||
}
|
||||
let function = scope.context.alloc_function(Function {
|
||||
id: scope.context.get_id(),
|
||||
name: RefCell::new(value.identifier.clone()),
|
||||
output,
|
||||
arguments,
|
||||
circuit: Cell::new(None),
|
||||
body: Cell::new(None),
|
||||
qualifier,
|
||||
scope: new_scope,
|
||||
span: Some(value.span.clone()),
|
||||
annotations: value.annotations.clone(),
|
||||
});
|
||||
function.scope.function.replace(Some(function));
|
||||
|
||||
Ok(function)
|
||||
}
|
||||
|
||||
pub(super) fn fill_from_ast(self: &'a Function<'a>, value: &leo_ast::Function) -> Result<(), AsgConvertError> {
|
||||
if self.qualifier != FunctionQualifier::Static {
|
||||
let circuit = self.circuit.get();
|
||||
let self_variable = self.scope.context.alloc_variable(RefCell::new(crate::InnerVariable {
|
||||
id: self.scope.context.get_id(),
|
||||
name: Identifier::new("self".into()),
|
||||
type_: Type::Circuit(circuit.as_ref().unwrap()),
|
||||
mutable: self.qualifier == FunctionQualifier::MutSelfRef,
|
||||
const_: false,
|
||||
declaration: crate::VariableDeclaration::Parameter,
|
||||
references: vec![],
|
||||
assignments: vec![],
|
||||
}));
|
||||
self.scope
|
||||
.variables
|
||||
.borrow_mut()
|
||||
.insert("self".to_string(), self_variable);
|
||||
}
|
||||
for (name, argument) in self.arguments.iter() {
|
||||
self.scope.variables.borrow_mut().insert(name.clone(), argument.get());
|
||||
}
|
||||
|
||||
let main_block = BlockStatement::from_ast(self.scope, &value.block, None)?;
|
||||
let mut director = MonoidalDirector::new(ReturnPathReducer::new());
|
||||
if !director.reduce_block(&main_block).0 && !self.output.is_unit() {
|
||||
return Err(AsgConvertError::function_missing_return(
|
||||
&self.name.borrow().name,
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
|
||||
#[allow(clippy::never_loop)] // TODO @Protryon: How should we return multiple errors?
|
||||
for (span, error) in director.reducer().errors {
|
||||
return Err(AsgConvertError::function_return_validation(
|
||||
&self.name.borrow().name,
|
||||
&error,
|
||||
&span,
|
||||
));
|
||||
}
|
||||
|
||||
self.body
|
||||
.replace(Some(self.scope.context.alloc_statement(Statement::Block(main_block))));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_test(&self) -> bool {
|
||||
self.annotations.iter().any(|x| x.name.name.as_ref() == "test")
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::Function> for &Function<'a> {
|
||||
fn into(self) -> leo_ast::Function {
|
||||
let input = self
|
||||
.arguments
|
||||
.iter()
|
||||
.map(|(_, variable)| {
|
||||
let variable = variable.get().borrow();
|
||||
leo_ast::FunctionInput::Variable(leo_ast::FunctionInputVariable {
|
||||
identifier: variable.name.clone(),
|
||||
mutable: variable.mutable,
|
||||
const_: variable.const_,
|
||||
type_: (&variable.type_).into(),
|
||||
span: Span::default(),
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
let (body, span) = match self.body.get() {
|
||||
Some(Statement::Block(block)) => (block.into(), block.span.clone().unwrap_or_default()),
|
||||
Some(_) => unimplemented!(),
|
||||
None => (
|
||||
leo_ast::Block {
|
||||
statements: vec![],
|
||||
span: Default::default(),
|
||||
},
|
||||
Default::default(),
|
||||
),
|
||||
};
|
||||
let output: Type = self.output.clone();
|
||||
leo_ast::Function {
|
||||
identifier: self.name.borrow().clone(),
|
||||
input,
|
||||
block: body,
|
||||
output: Some((&output).into()),
|
||||
span,
|
||||
annotations: self.annotations.clone(),
|
||||
}
|
||||
}
|
||||
}
|
482
asg/src/program/mod.rs
Normal file
482
asg/src/program/mod.rs
Normal file
@ -0,0 +1,482 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! This module defines the program node for an asg.
|
||||
//!
|
||||
//!
|
||||
|
||||
mod circuit;
|
||||
pub use circuit::*;
|
||||
|
||||
mod function;
|
||||
pub use function::*;
|
||||
|
||||
use crate::{
|
||||
node::FromAst,
|
||||
ArenaNode,
|
||||
AsgContext,
|
||||
AsgConvertError,
|
||||
DefinitionStatement,
|
||||
ImportResolver,
|
||||
Input,
|
||||
Scope,
|
||||
Statement,
|
||||
};
|
||||
use leo_ast::{Identifier, PackageAccess, PackageOrPackages, Span};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
/// Stores the Leo program abstract semantic graph (ASG).
|
||||
#[derive(Clone)]
|
||||
pub struct Program<'a> {
|
||||
pub context: AsgContext<'a>,
|
||||
|
||||
/// The unique id of the program.
|
||||
pub id: u32,
|
||||
|
||||
/// The program file name.
|
||||
pub name: String,
|
||||
|
||||
/// The packages imported by this program.
|
||||
/// these should generally not be accessed directly, but through scoped imports
|
||||
pub imported_modules: IndexMap<String, Program<'a>>,
|
||||
|
||||
/// Maps function name => function code block.
|
||||
pub functions: IndexMap<String, &'a Function<'a>>,
|
||||
|
||||
/// Maps global constant name => global const code block.
|
||||
pub global_consts: IndexMap<String, &'a DefinitionStatement<'a>>,
|
||||
|
||||
/// Maps circuit name => circuit code block.
|
||||
pub circuits: IndexMap<String, &'a Circuit<'a>>,
|
||||
|
||||
pub scope: &'a Scope<'a>,
|
||||
}
|
||||
|
||||
/// Enumerates what names are imported from a package.
|
||||
#[derive(Clone)]
|
||||
enum ImportSymbol {
|
||||
/// Import the symbol by name.
|
||||
Direct(String),
|
||||
|
||||
/// Import the symbol by name and store it under an alias.
|
||||
Alias(String, String), // from remote -> to local
|
||||
|
||||
/// Import all symbols from the package.
|
||||
All,
|
||||
}
|
||||
|
||||
fn resolve_import_package(
|
||||
output: &mut Vec<(Vec<String>, ImportSymbol, Span)>,
|
||||
mut package_segments: Vec<String>,
|
||||
package_or_packages: &PackageOrPackages,
|
||||
) {
|
||||
match package_or_packages {
|
||||
PackageOrPackages::Package(package) => {
|
||||
package_segments.push(package.name.name.to_string());
|
||||
resolve_import_package_access(output, package_segments, &package.access);
|
||||
}
|
||||
PackageOrPackages::Packages(packages) => {
|
||||
package_segments.push(packages.name.name.to_string());
|
||||
for access in packages.accesses.clone() {
|
||||
resolve_import_package_access(output, package_segments.clone(), &access);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_import_package_access(
|
||||
output: &mut Vec<(Vec<String>, ImportSymbol, Span)>,
|
||||
mut package_segments: Vec<String>,
|
||||
package: &PackageAccess,
|
||||
) {
|
||||
match package {
|
||||
PackageAccess::Star(span) => {
|
||||
output.push((package_segments, ImportSymbol::All, span.clone()));
|
||||
}
|
||||
PackageAccess::SubPackage(subpackage) => {
|
||||
resolve_import_package(
|
||||
output,
|
||||
package_segments,
|
||||
&PackageOrPackages::Package(*(*subpackage).clone()),
|
||||
);
|
||||
}
|
||||
PackageAccess::Symbol(symbol) => {
|
||||
let span = symbol.symbol.span.clone();
|
||||
let symbol = if let Some(alias) = symbol.alias.as_ref() {
|
||||
ImportSymbol::Alias(symbol.symbol.name.to_string(), alias.name.to_string())
|
||||
} else {
|
||||
ImportSymbol::Direct(symbol.symbol.name.to_string())
|
||||
};
|
||||
output.push((package_segments, symbol, span));
|
||||
}
|
||||
PackageAccess::Multiple(packages) => {
|
||||
package_segments.push(packages.name.name.to_string());
|
||||
for subaccess in packages.accesses.iter() {
|
||||
resolve_import_package_access(output, package_segments.clone(), &subaccess);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Program<'a> {
|
||||
/// Returns a new Leo program ASG from the given Leo program AST and its imports.
|
||||
///
|
||||
/// Stages:
|
||||
/// 1. resolve imports into super scope
|
||||
/// 2. finalize declared types
|
||||
/// 3. finalize declared functions
|
||||
/// 4. resolve all asg nodes
|
||||
///
|
||||
pub fn new<T: ImportResolver<'a>>(
|
||||
context: AsgContext<'a>,
|
||||
program: &leo_ast::Program,
|
||||
import_resolver: &mut T,
|
||||
) -> Result<Program<'a>, AsgConvertError> {
|
||||
// Recursively extract imported symbols.
|
||||
let mut imported_symbols: Vec<(Vec<String>, ImportSymbol, Span)> = vec![];
|
||||
for import in program.imports.iter() {
|
||||
resolve_import_package(&mut imported_symbols, vec![], &import.package_or_packages);
|
||||
}
|
||||
|
||||
// Create package list.
|
||||
let mut deduplicated_imports: IndexMap<Vec<String>, Span> = IndexMap::new();
|
||||
for (package, _symbol, span) in imported_symbols.iter() {
|
||||
deduplicated_imports.insert(package.clone(), span.clone());
|
||||
}
|
||||
|
||||
let mut wrapped_resolver = crate::CoreImportResolver::new(import_resolver);
|
||||
|
||||
// Load imported programs.
|
||||
let mut resolved_packages: IndexMap<Vec<String>, Program> = IndexMap::new();
|
||||
for (package, span) in deduplicated_imports.iter() {
|
||||
let pretty_package = package.join(".");
|
||||
|
||||
let resolved_package = match wrapped_resolver.resolve_package(
|
||||
context,
|
||||
&package.iter().map(|x| &**x).collect::<Vec<_>>()[..],
|
||||
span,
|
||||
)? {
|
||||
Some(x) => x,
|
||||
None => return Err(AsgConvertError::unresolved_import(&*pretty_package, &Span::default())),
|
||||
};
|
||||
|
||||
resolved_packages.insert(package.clone(), resolved_package);
|
||||
}
|
||||
|
||||
let mut imported_functions: IndexMap<String, &'a Function<'a>> = IndexMap::new();
|
||||
let mut imported_circuits: IndexMap<String, &'a Circuit<'a>> = IndexMap::new();
|
||||
let mut imported_global_consts: IndexMap<String, &'a DefinitionStatement<'a>> = IndexMap::new();
|
||||
|
||||
// Prepare locally relevant scope of imports.
|
||||
for (package, symbol, span) in imported_symbols.into_iter() {
|
||||
let pretty_package = package.join(".");
|
||||
|
||||
let resolved_package = resolved_packages
|
||||
.get(&package)
|
||||
.expect("could not find preloaded package");
|
||||
match symbol {
|
||||
ImportSymbol::All => {
|
||||
imported_functions.extend(resolved_package.functions.clone().into_iter());
|
||||
imported_circuits.extend(resolved_package.circuits.clone().into_iter());
|
||||
imported_global_consts.extend(resolved_package.global_consts.clone().into_iter());
|
||||
}
|
||||
ImportSymbol::Direct(name) => {
|
||||
if let Some(function) = resolved_package.functions.get(&name) {
|
||||
imported_functions.insert(name.clone(), *function);
|
||||
} else if let Some(circuit) = resolved_package.circuits.get(&name) {
|
||||
imported_circuits.insert(name.clone(), *circuit);
|
||||
} else if let Some(global_const) = resolved_package.global_consts.get(&name) {
|
||||
imported_global_consts.insert(name.clone(), *global_const);
|
||||
} else {
|
||||
return Err(AsgConvertError::unresolved_import(
|
||||
&*format!("{}.{}", pretty_package, name),
|
||||
&span,
|
||||
));
|
||||
}
|
||||
}
|
||||
ImportSymbol::Alias(name, alias) => {
|
||||
if let Some(function) = resolved_package.functions.get(&name) {
|
||||
imported_functions.insert(alias.clone(), *function);
|
||||
} else if let Some(circuit) = resolved_package.circuits.get(&name) {
|
||||
imported_circuits.insert(alias.clone(), *circuit);
|
||||
} else if let Some(global_const) = resolved_package.global_consts.get(&name) {
|
||||
imported_global_consts.insert(alias.clone(), *global_const);
|
||||
} else {
|
||||
return Err(AsgConvertError::unresolved_import(
|
||||
&*format!("{}.{}", pretty_package, name),
|
||||
&span,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let import_scope = match context.arena.alloc(ArenaNode::Scope(Box::new(Scope {
|
||||
context,
|
||||
id: context.get_id(),
|
||||
parent_scope: Cell::new(None),
|
||||
circuit_self: Cell::new(None),
|
||||
variables: RefCell::new(IndexMap::new()),
|
||||
functions: RefCell::new(imported_functions),
|
||||
global_consts: RefCell::new(imported_global_consts),
|
||||
circuits: RefCell::new(imported_circuits),
|
||||
function: Cell::new(None),
|
||||
input: Cell::new(None),
|
||||
}))) {
|
||||
ArenaNode::Scope(c) => c,
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
let scope = import_scope.context.alloc_scope(Scope {
|
||||
context,
|
||||
input: Cell::new(Some(Input::new(import_scope))), // we use import_scope to avoid recursive scope ref here
|
||||
id: context.get_id(),
|
||||
parent_scope: Cell::new(Some(import_scope)),
|
||||
circuit_self: Cell::new(None),
|
||||
variables: RefCell::new(IndexMap::new()),
|
||||
functions: RefCell::new(IndexMap::new()),
|
||||
global_consts: RefCell::new(IndexMap::new()),
|
||||
circuits: RefCell::new(IndexMap::new()),
|
||||
function: Cell::new(None),
|
||||
});
|
||||
|
||||
// Prepare header-like scope entries.
|
||||
for (name, circuit) in program.circuits.iter() {
|
||||
assert_eq!(name.name, circuit.circuit_name.name);
|
||||
let asg_circuit = Circuit::init(scope, circuit)?;
|
||||
|
||||
scope.circuits.borrow_mut().insert(name.name.to_string(), asg_circuit);
|
||||
}
|
||||
|
||||
// Second pass for circuit members.
|
||||
for (name, circuit) in program.circuits.iter() {
|
||||
assert_eq!(name.name, circuit.circuit_name.name);
|
||||
let asg_circuit = Circuit::init_member(scope, circuit)?;
|
||||
|
||||
scope.circuits.borrow_mut().insert(name.name.to_string(), asg_circuit);
|
||||
}
|
||||
|
||||
for (name, function) in program.functions.iter() {
|
||||
assert_eq!(name.name, function.identifier.name);
|
||||
let function = Function::init(scope, function)?;
|
||||
|
||||
scope.functions.borrow_mut().insert(name.name.to_string(), function);
|
||||
}
|
||||
|
||||
for (name, global_const) in program.global_consts.iter() {
|
||||
global_const
|
||||
.variable_names
|
||||
.iter()
|
||||
.for_each(|variable_name| assert!(name.contains(&variable_name.identifier.name.to_string())));
|
||||
let gc = <&Statement<'a>>::from_ast(scope, global_const, None)?;
|
||||
if let Statement::Definition(gc) = gc {
|
||||
scope.global_consts.borrow_mut().insert(name.clone(), gc);
|
||||
}
|
||||
}
|
||||
|
||||
// Load concrete definitions.
|
||||
let mut global_consts = IndexMap::new();
|
||||
for (name, global_const) in program.global_consts.iter() {
|
||||
global_const
|
||||
.variable_names
|
||||
.iter()
|
||||
.for_each(|variable_name| assert!(name.contains(&variable_name.identifier.name.to_string())));
|
||||
let asg_global_const = *scope.global_consts.borrow().get(name).unwrap();
|
||||
|
||||
global_consts.insert(name.clone(), asg_global_const);
|
||||
}
|
||||
|
||||
let mut functions = IndexMap::new();
|
||||
for (name, function) in program.functions.iter() {
|
||||
assert_eq!(name.name, function.identifier.name);
|
||||
let asg_function = *scope.functions.borrow().get(name.name.as_ref()).unwrap();
|
||||
|
||||
asg_function.fill_from_ast(function)?;
|
||||
|
||||
let name = name.name.to_string();
|
||||
|
||||
if functions.contains_key(&name) {
|
||||
return Err(AsgConvertError::duplicate_function_definition(&name, &function.span));
|
||||
}
|
||||
|
||||
functions.insert(name, asg_function);
|
||||
}
|
||||
|
||||
let mut circuits = IndexMap::new();
|
||||
for (name, circuit) in program.circuits.iter() {
|
||||
assert_eq!(name.name, circuit.circuit_name.name);
|
||||
let asg_circuit = *scope.circuits.borrow().get(name.name.as_ref()).unwrap();
|
||||
|
||||
asg_circuit.fill_from_ast(circuit)?;
|
||||
|
||||
circuits.insert(name.name.to_string(), asg_circuit);
|
||||
}
|
||||
|
||||
Ok(Program {
|
||||
context,
|
||||
id: context.get_id(),
|
||||
name: program.name.clone(),
|
||||
functions,
|
||||
global_consts,
|
||||
circuits,
|
||||
imported_modules: resolved_packages
|
||||
.into_iter()
|
||||
.map(|(package, program)| (package.join("."), program))
|
||||
.collect(),
|
||||
scope,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn set_core_mapping(&self, mapping: &str) {
|
||||
for (_, circuit) in self.circuits.iter() {
|
||||
circuit.core_mapping.replace(Some(mapping.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct InternalIdentifierGenerator {
|
||||
next: usize,
|
||||
}
|
||||
|
||||
impl Iterator for InternalIdentifierGenerator {
|
||||
type Item = String;
|
||||
|
||||
fn next(&mut self) -> Option<String> {
|
||||
let out = format!("$_{}_", self.next);
|
||||
self.next += 1;
|
||||
Some(out)
|
||||
}
|
||||
}
|
||||
/// Returns an AST from the given ASG program.
|
||||
pub fn reform_ast<'a>(program: &Program<'a>) -> leo_ast::Program {
|
||||
let mut all_programs: IndexMap<String, Program> = IndexMap::new();
|
||||
let mut program_stack = program.imported_modules.clone();
|
||||
while let Some((module, program)) = program_stack.pop() {
|
||||
if all_programs.contains_key(&module) {
|
||||
continue;
|
||||
}
|
||||
all_programs.insert(module, program.clone());
|
||||
program_stack.extend(program.imported_modules.clone());
|
||||
}
|
||||
all_programs.insert("".to_string(), program.clone());
|
||||
let core_programs: Vec<_> = all_programs
|
||||
.iter()
|
||||
.filter(|(module, _)| module.starts_with("core."))
|
||||
.map(|(module, program)| (module.clone(), program.clone()))
|
||||
.collect();
|
||||
all_programs.retain(|module, _| !module.starts_with("core."));
|
||||
|
||||
let mut all_circuits: IndexMap<String, &'a Circuit<'a>> = IndexMap::new();
|
||||
let mut all_functions: IndexMap<String, &'a Function<'a>> = IndexMap::new();
|
||||
let mut all_global_consts: IndexMap<String, &'a DefinitionStatement<'a>> = IndexMap::new();
|
||||
let mut identifiers = InternalIdentifierGenerator { next: 0 };
|
||||
for (_, program) in all_programs.into_iter() {
|
||||
for (name, circuit) in program.circuits.iter() {
|
||||
let identifier = format!("{}{}", identifiers.next().unwrap(), name);
|
||||
circuit.name.borrow_mut().name = identifier.clone().into();
|
||||
all_circuits.insert(identifier, *circuit);
|
||||
}
|
||||
for (name, function) in program.functions.iter() {
|
||||
let identifier = if name == "main" {
|
||||
"main".to_string()
|
||||
} else {
|
||||
format!("{}{}", identifiers.next().unwrap(), name)
|
||||
};
|
||||
function.name.borrow_mut().name = identifier.clone().into();
|
||||
all_functions.insert(identifier, *function);
|
||||
}
|
||||
|
||||
for (name, global_const) in program.global_consts.iter() {
|
||||
let identifier = format!("{}{}", identifiers.next().unwrap(), name);
|
||||
all_global_consts.insert(identifier, *global_const);
|
||||
}
|
||||
}
|
||||
|
||||
leo_ast::Program {
|
||||
name: "ast_aggregate".to_string(),
|
||||
imports: core_programs
|
||||
.iter()
|
||||
.map(|(module, _)| leo_ast::ImportStatement {
|
||||
package_or_packages: leo_ast::PackageOrPackages::Package(leo_ast::Package {
|
||||
name: Identifier::new(module.clone().into()),
|
||||
access: leo_ast::PackageAccess::Star(Span::default()),
|
||||
span: Default::default(),
|
||||
}),
|
||||
span: Span::default(),
|
||||
})
|
||||
.collect(),
|
||||
expected_input: vec![],
|
||||
functions: all_functions
|
||||
.into_iter()
|
||||
.map(|(_, function)| (function.name.borrow().clone(), function.into()))
|
||||
.collect(),
|
||||
circuits: all_circuits
|
||||
.into_iter()
|
||||
.map(|(_, circuit)| (circuit.name.borrow().clone(), circuit.into()))
|
||||
.collect(),
|
||||
global_consts: all_global_consts
|
||||
.into_iter()
|
||||
.map(|(_, global_const)| {
|
||||
(
|
||||
global_const
|
||||
.variables
|
||||
.iter()
|
||||
.fold("".to_string(), |joined, variable_name| {
|
||||
format!("{}, {}", joined, variable_name.borrow().name.name)
|
||||
}),
|
||||
global_const.into(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::Program> for &Program<'a> {
|
||||
fn into(self) -> leo_ast::Program {
|
||||
leo_ast::Program {
|
||||
name: self.name.clone(),
|
||||
imports: vec![],
|
||||
expected_input: vec![],
|
||||
circuits: self
|
||||
.circuits
|
||||
.iter()
|
||||
.map(|(_, circuit)| (circuit.name.borrow().clone(), (*circuit).into()))
|
||||
.collect(),
|
||||
functions: self
|
||||
.functions
|
||||
.iter()
|
||||
.map(|(_, function)| (function.name.borrow().clone(), (*function).into()))
|
||||
.collect(),
|
||||
global_consts: self
|
||||
.global_consts
|
||||
.iter()
|
||||
.map(|(_, global_const)| {
|
||||
(
|
||||
global_const
|
||||
.variables
|
||||
.iter()
|
||||
.fold("".to_string(), |joined, variable_name| {
|
||||
format!("{}, {}", joined, variable_name.borrow().name.name)
|
||||
}),
|
||||
(*global_const).into(),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2020 Aleo Systems Inc.
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
@ -14,29 +14,26 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
pub mod console_assert;
|
||||
pub use console_assert::*;
|
||||
//! This module contains the reducer which iterates through ast nodes - converting them into
|
||||
//! asg nodes and saving relevant information.
|
||||
|
||||
pub mod console_debug;
|
||||
pub use console_debug::*;
|
||||
mod monoid;
|
||||
pub use monoid::*;
|
||||
|
||||
pub mod console_error;
|
||||
pub use console_error::*;
|
||||
mod monoidal_director;
|
||||
pub use monoidal_director::*;
|
||||
|
||||
pub mod console_function;
|
||||
pub use console_function::*;
|
||||
mod monoidal_reducer;
|
||||
pub use monoidal_reducer::*;
|
||||
|
||||
pub mod console_function_call;
|
||||
pub use console_function_call::*;
|
||||
mod reconstructing_reducer;
|
||||
pub use reconstructing_reducer::*;
|
||||
|
||||
pub mod console_keyword;
|
||||
pub use console_keyword::*;
|
||||
mod reconstructing_director;
|
||||
pub use reconstructing_director::*;
|
||||
|
||||
pub mod console_log;
|
||||
pub use console_log::*;
|
||||
mod visitor;
|
||||
pub use visitor::*;
|
||||
|
||||
pub mod formatted_container;
|
||||
pub use formatted_container::*;
|
||||
|
||||
pub mod formatted_string;
|
||||
pub use formatted_string::*;
|
||||
mod visitor_director;
|
||||
pub use visitor_director::*;
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2020 Aleo Systems Inc.
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
@ -14,19 +14,27 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{errors::ImportParserError, ImportParser};
|
||||
use leo_ast::Package;
|
||||
use super::*;
|
||||
|
||||
pub static CORE_PACKAGE_NAME: &str = "core";
|
||||
pub struct BoolAnd(pub bool);
|
||||
|
||||
impl ImportParser {
|
||||
///
|
||||
/// Import a core package and insert into the `ImportParser`.
|
||||
///
|
||||
pub fn parse_core_package(&mut self, package: &Package) -> Result<(), ImportParserError> {
|
||||
// Insert a core package into the `ImportParser`.
|
||||
self.insert_core_package(package)?;
|
||||
|
||||
Ok(())
|
||||
impl Default for BoolAnd {
|
||||
fn default() -> Self {
|
||||
BoolAnd(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl Monoid for BoolAnd {
|
||||
fn append(self, other: Self) -> Self {
|
||||
BoolAnd(self.0 && other.0)
|
||||
}
|
||||
|
||||
fn append_all(self, others: impl Iterator<Item = Self>) -> Self {
|
||||
for item in others {
|
||||
if !item.0 {
|
||||
return BoolAnd(false);
|
||||
}
|
||||
}
|
||||
BoolAnd(true)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2019-2020 Aleo Systems Inc.
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
@ -14,25 +14,30 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use leo_ast::Circuit;
|
||||
mod bool_and;
|
||||
pub use bool_and::*;
|
||||
|
||||
/// List of imported core circuit structs.
|
||||
/// This struct is created from a `CorePackageList`
|
||||
pub struct CoreCircuitStructList {
|
||||
/// [(circuit_name, circuit_struct)]
|
||||
symbols: Vec<(String, Circuit)>,
|
||||
}
|
||||
mod set_append;
|
||||
pub use set_append::*;
|
||||
|
||||
impl CoreCircuitStructList {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self { symbols: vec![] }
|
||||
mod vec_append;
|
||||
pub use vec_append::*;
|
||||
|
||||
pub trait Monoid: Default {
|
||||
fn append(self, other: Self) -> Self;
|
||||
|
||||
fn append_all(self, others: impl Iterator<Item = Self>) -> Self {
|
||||
let mut current = self;
|
||||
for item in others {
|
||||
current = current.append(item);
|
||||
}
|
||||
current
|
||||
}
|
||||
|
||||
pub(crate) fn push(&mut self, name: String, circuit: Circuit) {
|
||||
self.symbols.push((name, circuit))
|
||||
}
|
||||
|
||||
pub fn symbols(&self) -> impl Iterator<Item = &(String, Circuit)> {
|
||||
self.symbols.iter()
|
||||
fn append_option(self, other: Option<Self>) -> Self {
|
||||
match other {
|
||||
None => self,
|
||||
Some(other) => self.append(other),
|
||||
}
|
||||
}
|
||||
}
|
51
asg/src/reducer/monoid/set_append.rs
Normal file
51
asg/src/reducer/monoid/set_append.rs
Normal file
@ -0,0 +1,51 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use super::*;
|
||||
|
||||
use indexmap::IndexSet;
|
||||
use std::hash::Hash;
|
||||
|
||||
pub struct SetAppend<T: Hash + Eq + 'static>(IndexSet<T>);
|
||||
|
||||
impl<T: Hash + Eq + 'static> Default for SetAppend<T> {
|
||||
fn default() -> Self {
|
||||
Self(IndexSet::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash + Eq + 'static> Monoid for SetAppend<T> {
|
||||
fn append(mut self, other: Self) -> Self {
|
||||
self.0.extend(other.0);
|
||||
SetAppend(self.0)
|
||||
}
|
||||
|
||||
fn append_all(mut self, others: impl Iterator<Item = Self>) -> Self {
|
||||
let all: Vec<IndexSet<T>> = others.map(|x| x.0).collect();
|
||||
let total_size = all.iter().fold(0, |acc, v| acc + v.len());
|
||||
self.0.reserve(total_size);
|
||||
for item in all.into_iter() {
|
||||
self.0.extend(item);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hash + Eq + 'static> Into<IndexSet<T>> for SetAppend<T> {
|
||||
fn into(self) -> IndexSet<T> {
|
||||
self.0
|
||||
}
|
||||
}
|
48
asg/src/reducer/monoid/vec_append.rs
Normal file
48
asg/src/reducer/monoid/vec_append.rs
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct VecAppend<T>(Vec<T>);
|
||||
|
||||
impl<T> Default for VecAppend<T> {
|
||||
fn default() -> Self {
|
||||
Self(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Monoid for VecAppend<T> {
|
||||
fn append(mut self, other: Self) -> Self {
|
||||
self.0.extend(other.0);
|
||||
VecAppend(self.0)
|
||||
}
|
||||
|
||||
fn append_all(mut self, others: impl Iterator<Item = Self>) -> Self {
|
||||
let all: Vec<Vec<T>> = others.map(|x| x.0).collect();
|
||||
let total_size = all.iter().fold(0, |acc, v| acc + v.len());
|
||||
self.0.reserve(total_size);
|
||||
for item in all.into_iter() {
|
||||
self.0.extend(item);
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Into<Vec<T>> for VecAppend<T> {
|
||||
fn into(self) -> Vec<T> {
|
||||
self.0
|
||||
}
|
||||
}
|
315
asg/src/reducer/monoidal_director.rs
Normal file
315
asg/src/reducer/monoidal_director.rs
Normal file
@ -0,0 +1,315 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use super::*;
|
||||
use crate::{expression::*, program::*, statement::*};
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub struct MonoidalDirector<'a, T: Monoid, R: MonoidalReducerExpression<'a, T>> {
|
||||
reducer: R,
|
||||
_monoid: PhantomData<&'a T>,
|
||||
}
|
||||
|
||||
impl<'a, T: Monoid, R: MonoidalReducerExpression<'a, T>> MonoidalDirector<'a, T, R> {
|
||||
pub fn new(reducer: R) -> Self {
|
||||
Self {
|
||||
reducer,
|
||||
_monoid: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reducer(self) -> R {
|
||||
self.reducer
|
||||
}
|
||||
|
||||
pub fn reduce_expression(&mut self, input: &'a Expression<'a>) -> T {
|
||||
let value = match input {
|
||||
Expression::ArrayAccess(e) => self.reduce_array_access(e),
|
||||
Expression::ArrayInit(e) => self.reduce_array_init(e),
|
||||
Expression::ArrayInline(e) => self.reduce_array_inline(e),
|
||||
Expression::ArrayRangeAccess(e) => self.reduce_array_range_access(e),
|
||||
Expression::Binary(e) => self.reduce_binary(e),
|
||||
Expression::Call(e) => self.reduce_call(e),
|
||||
Expression::CircuitAccess(e) => self.reduce_circuit_access(e),
|
||||
Expression::CircuitInit(e) => self.reduce_circuit_init(e),
|
||||
Expression::Ternary(e) => self.reduce_ternary_expression(e),
|
||||
Expression::Cast(e) => self.reduce_cast_expression(e),
|
||||
Expression::Constant(e) => self.reduce_constant(e),
|
||||
Expression::TupleAccess(e) => self.reduce_tuple_access(e),
|
||||
Expression::TupleInit(e) => self.reduce_tuple_init(e),
|
||||
Expression::Unary(e) => self.reduce_unary(e),
|
||||
Expression::VariableRef(e) => self.reduce_variable_ref(e),
|
||||
};
|
||||
|
||||
self.reducer.reduce_expression(input, value)
|
||||
}
|
||||
|
||||
pub fn reduce_array_access(&mut self, input: &ArrayAccessExpression<'a>) -> T {
|
||||
let array = self.reduce_expression(input.array.get());
|
||||
let index = self.reduce_expression(input.index.get());
|
||||
|
||||
self.reducer.reduce_array_access(input, array, index)
|
||||
}
|
||||
|
||||
pub fn reduce_array_init(&mut self, input: &ArrayInitExpression<'a>) -> T {
|
||||
let element = self.reduce_expression(input.element.get());
|
||||
|
||||
self.reducer.reduce_array_init(input, element)
|
||||
}
|
||||
|
||||
pub fn reduce_array_inline(&mut self, input: &ArrayInlineExpression<'a>) -> T {
|
||||
let elements = input
|
||||
.elements
|
||||
.iter()
|
||||
.map(|(x, _)| self.reduce_expression(x.get()))
|
||||
.collect();
|
||||
|
||||
self.reducer.reduce_array_inline(input, elements)
|
||||
}
|
||||
|
||||
pub fn reduce_array_range_access(&mut self, input: &ArrayRangeAccessExpression<'a>) -> T {
|
||||
let array = self.reduce_expression(input.array.get());
|
||||
let left = input.left.get().map(|e| self.reduce_expression(e));
|
||||
let right = input.right.get().map(|e| self.reduce_expression(e));
|
||||
|
||||
self.reducer.reduce_array_range_access(input, array, left, right)
|
||||
}
|
||||
|
||||
pub fn reduce_binary(&mut self, input: &BinaryExpression<'a>) -> T {
|
||||
let left = self.reduce_expression(input.left.get());
|
||||
let right = self.reduce_expression(input.right.get());
|
||||
|
||||
self.reducer.reduce_binary(input, left, right)
|
||||
}
|
||||
|
||||
pub fn reduce_call(&mut self, input: &CallExpression<'a>) -> T {
|
||||
let target = input.target.get().map(|e| self.reduce_expression(e));
|
||||
let arguments = input
|
||||
.arguments
|
||||
.iter()
|
||||
.map(|e| self.reduce_expression(e.get()))
|
||||
.collect();
|
||||
|
||||
self.reducer.reduce_call(input, target, arguments)
|
||||
}
|
||||
|
||||
pub fn reduce_circuit_access(&mut self, input: &CircuitAccessExpression<'a>) -> T {
|
||||
let target = input.target.get().map(|e| self.reduce_expression(e));
|
||||
|
||||
self.reducer.reduce_circuit_access(input, target)
|
||||
}
|
||||
|
||||
pub fn reduce_circuit_init(&mut self, input: &CircuitInitExpression<'a>) -> T {
|
||||
let values = input
|
||||
.values
|
||||
.iter()
|
||||
.map(|(_, e)| self.reduce_expression(e.get()))
|
||||
.collect();
|
||||
|
||||
self.reducer.reduce_circuit_init(input, values)
|
||||
}
|
||||
|
||||
pub fn reduce_ternary_expression(&mut self, input: &TernaryExpression<'a>) -> T {
|
||||
let condition = self.reduce_expression(input.condition.get());
|
||||
let if_true = self.reduce_expression(input.if_true.get());
|
||||
let if_false = self.reduce_expression(input.if_false.get());
|
||||
|
||||
self.reducer
|
||||
.reduce_ternary_expression(input, condition, if_true, if_false)
|
||||
}
|
||||
|
||||
pub fn reduce_cast_expression(&mut self, input: &CastExpression<'a>) -> T {
|
||||
let inner = self.reduce_expression(input.inner.get());
|
||||
|
||||
self.reducer.reduce_cast_expression(input, inner)
|
||||
}
|
||||
|
||||
pub fn reduce_constant(&mut self, input: &Constant<'a>) -> T {
|
||||
self.reducer.reduce_constant(input)
|
||||
}
|
||||
|
||||
pub fn reduce_tuple_access(&mut self, input: &TupleAccessExpression<'a>) -> T {
|
||||
let tuple_ref = self.reduce_expression(input.tuple_ref.get());
|
||||
|
||||
self.reducer.reduce_tuple_access(input, tuple_ref)
|
||||
}
|
||||
|
||||
pub fn reduce_tuple_init(&mut self, input: &TupleInitExpression<'a>) -> T {
|
||||
let values = input.elements.iter().map(|e| self.reduce_expression(e.get())).collect();
|
||||
|
||||
self.reducer.reduce_tuple_init(input, values)
|
||||
}
|
||||
|
||||
pub fn reduce_unary(&mut self, input: &UnaryExpression<'a>) -> T {
|
||||
let inner = self.reduce_expression(input.inner.get());
|
||||
|
||||
self.reducer.reduce_unary(input, inner)
|
||||
}
|
||||
|
||||
pub fn reduce_variable_ref(&mut self, input: &VariableRef<'a>) -> T {
|
||||
self.reducer.reduce_variable_ref(input)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Monoid, R: MonoidalReducerStatement<'a, T>> MonoidalDirector<'a, T, R> {
|
||||
pub fn reduce_statement(&mut self, input: &'a Statement<'a>) -> T {
|
||||
let value = match input {
|
||||
Statement::Assign(s) => self.reduce_assign(s),
|
||||
Statement::Block(s) => self.reduce_block(s),
|
||||
Statement::Conditional(s) => self.reduce_conditional_statement(s),
|
||||
Statement::Console(s) => self.reduce_console(s),
|
||||
Statement::Definition(s) => self.reduce_definition(s),
|
||||
Statement::Expression(s) => self.reduce_expression_statement(s),
|
||||
Statement::Iteration(s) => self.reduce_iteration(s),
|
||||
Statement::Return(s) => self.reduce_return(s),
|
||||
Statement::Empty(_) => T::default(),
|
||||
};
|
||||
|
||||
self.reducer.reduce_statement(input, value)
|
||||
}
|
||||
|
||||
pub fn reduce_assign_access(&mut self, input: &AssignAccess<'a>) -> T {
|
||||
let (left, right) = match input {
|
||||
AssignAccess::ArrayRange(left, right) => (
|
||||
left.get().map(|e| self.reduce_expression(e)),
|
||||
right.get().map(|e| self.reduce_expression(e)),
|
||||
),
|
||||
AssignAccess::ArrayIndex(index) => (Some(self.reduce_expression(index.get())), None),
|
||||
_ => (None, None),
|
||||
};
|
||||
|
||||
self.reducer.reduce_assign_access(input, left, right)
|
||||
}
|
||||
|
||||
pub fn reduce_assign(&mut self, input: &AssignStatement<'a>) -> T {
|
||||
let accesses = input
|
||||
.target_accesses
|
||||
.iter()
|
||||
.map(|x| self.reduce_assign_access(x))
|
||||
.collect();
|
||||
let value = self.reduce_expression(input.value.get());
|
||||
|
||||
self.reducer.reduce_assign(input, accesses, value)
|
||||
}
|
||||
|
||||
pub fn reduce_block(&mut self, input: &BlockStatement<'a>) -> T {
|
||||
let statements = input
|
||||
.statements
|
||||
.iter()
|
||||
.map(|x| self.reduce_statement(x.get()))
|
||||
.collect();
|
||||
|
||||
self.reducer.reduce_block(input, statements)
|
||||
}
|
||||
|
||||
pub fn reduce_conditional_statement(&mut self, input: &ConditionalStatement<'a>) -> T {
|
||||
let condition = self.reduce_expression(input.condition.get());
|
||||
let if_true = self.reduce_statement(input.result.get());
|
||||
let if_false = input.next.get().map(|s| self.reduce_statement(s));
|
||||
|
||||
self.reducer
|
||||
.reduce_conditional_statement(input, condition, if_true, if_false)
|
||||
}
|
||||
|
||||
pub fn reduce_formatted_string(&mut self, input: &FormatString<'a>) -> T {
|
||||
let parameters = input
|
||||
.parameters
|
||||
.iter()
|
||||
.map(|e| self.reduce_expression(e.get()))
|
||||
.collect();
|
||||
|
||||
self.reducer.reduce_formatted_string(input, parameters)
|
||||
}
|
||||
|
||||
pub fn reduce_console(&mut self, input: &ConsoleStatement<'a>) -> T {
|
||||
let argument = match &input.function {
|
||||
ConsoleFunction::Assert(e) => self.reduce_expression(e.get()),
|
||||
ConsoleFunction::Debug(f) | ConsoleFunction::Error(f) | ConsoleFunction::Log(f) => {
|
||||
self.reduce_formatted_string(f)
|
||||
}
|
||||
};
|
||||
|
||||
self.reducer.reduce_console(input, argument)
|
||||
}
|
||||
|
||||
pub fn reduce_definition(&mut self, input: &DefinitionStatement<'a>) -> T {
|
||||
let value = self.reduce_expression(input.value.get());
|
||||
|
||||
self.reducer.reduce_definition(input, value)
|
||||
}
|
||||
|
||||
pub fn reduce_expression_statement(&mut self, input: &ExpressionStatement<'a>) -> T {
|
||||
let value = self.reduce_expression(input.expression.get());
|
||||
|
||||
self.reducer.reduce_expression_statement(input, value)
|
||||
}
|
||||
|
||||
pub fn reduce_iteration(&mut self, input: &IterationStatement<'a>) -> T {
|
||||
let start = self.reduce_expression(input.start.get());
|
||||
let stop = self.reduce_expression(input.stop.get());
|
||||
let body = self.reduce_statement(input.body.get());
|
||||
|
||||
self.reducer.reduce_iteration(input, start, stop, body)
|
||||
}
|
||||
|
||||
pub fn reduce_return(&mut self, input: &ReturnStatement<'a>) -> T {
|
||||
let value = self.reduce_expression(input.expression.get());
|
||||
|
||||
self.reducer.reduce_return(input, value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Monoid, R: MonoidalReducerProgram<'a, T>> MonoidalDirector<'a, T, R> {
|
||||
pub fn reduce_function(&mut self, input: &'a Function<'a>) -> T {
|
||||
let body = input.body.get().map(|s| self.reduce_statement(s)).unwrap_or_default();
|
||||
|
||||
self.reducer.reduce_function(input, body)
|
||||
}
|
||||
|
||||
pub fn reduce_circuit_member(&mut self, input: &CircuitMember<'a>) -> T {
|
||||
let function = match input {
|
||||
CircuitMember::Function(f) => Some(self.reduce_function(f)),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
self.reducer.reduce_circuit_member(input, function)
|
||||
}
|
||||
|
||||
pub fn reduce_circuit(&mut self, input: &'a Circuit<'a>) -> T {
|
||||
let members = input
|
||||
.members
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|(_, member)| self.reduce_circuit_member(member))
|
||||
.collect();
|
||||
|
||||
self.reducer.reduce_circuit(input, members)
|
||||
}
|
||||
|
||||
pub fn reduce_program(&mut self, input: &Program<'a>) -> T {
|
||||
let imported_modules = input
|
||||
.imported_modules
|
||||
.iter()
|
||||
.map(|(_, import)| self.reduce_program(import))
|
||||
.collect();
|
||||
let functions = input.functions.iter().map(|(_, f)| self.reduce_function(f)).collect();
|
||||
let circuits = input.circuits.iter().map(|(_, c)| self.reduce_circuit(c)).collect();
|
||||
|
||||
self.reducer
|
||||
.reduce_program(&input, imported_modules, functions, circuits)
|
||||
}
|
||||
}
|
166
asg/src/reducer/monoidal_reducer.rs
Normal file
166
asg/src/reducer/monoidal_reducer.rs
Normal file
@ -0,0 +1,166 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{expression::*, program::*, statement::*, Monoid};
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub trait MonoidalReducerExpression<'a, T: Monoid> {
|
||||
fn reduce_expression(&mut self, input: &'a Expression<'a>, value: T) -> T {
|
||||
value
|
||||
}
|
||||
|
||||
fn reduce_array_access(&mut self, input: &ArrayAccessExpression<'a>, array: T, index: T) -> T {
|
||||
array.append(index)
|
||||
}
|
||||
|
||||
fn reduce_array_init(&mut self, input: &ArrayInitExpression<'a>, element: T) -> T {
|
||||
element
|
||||
}
|
||||
|
||||
fn reduce_array_inline(&mut self, input: &ArrayInlineExpression<'a>, elements: Vec<T>) -> T {
|
||||
T::default().append_all(elements.into_iter())
|
||||
}
|
||||
|
||||
fn reduce_array_range_access(
|
||||
&mut self,
|
||||
input: &ArrayRangeAccessExpression<'a>,
|
||||
array: T,
|
||||
left: Option<T>,
|
||||
right: Option<T>,
|
||||
) -> T {
|
||||
array.append_option(left).append_option(right)
|
||||
}
|
||||
|
||||
fn reduce_binary(&mut self, input: &BinaryExpression<'a>, left: T, right: T) -> T {
|
||||
left.append(right)
|
||||
}
|
||||
|
||||
fn reduce_call(&mut self, input: &CallExpression<'a>, target: Option<T>, arguments: Vec<T>) -> T {
|
||||
target.unwrap_or_default().append_all(arguments.into_iter())
|
||||
}
|
||||
|
||||
fn reduce_circuit_access(&mut self, input: &CircuitAccessExpression<'a>, target: Option<T>) -> T {
|
||||
target.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn reduce_circuit_init(&mut self, input: &CircuitInitExpression<'a>, values: Vec<T>) -> T {
|
||||
T::default().append_all(values.into_iter())
|
||||
}
|
||||
|
||||
fn reduce_ternary_expression(&mut self, input: &TernaryExpression<'a>, condition: T, if_true: T, if_false: T) -> T {
|
||||
condition.append(if_true).append(if_false)
|
||||
}
|
||||
|
||||
fn reduce_cast_expression(&mut self, input: &CastExpression<'a>, inner: T) -> T {
|
||||
inner
|
||||
}
|
||||
|
||||
fn reduce_constant(&mut self, input: &Constant<'a>) -> T {
|
||||
T::default()
|
||||
}
|
||||
|
||||
fn reduce_tuple_access(&mut self, input: &TupleAccessExpression<'a>, tuple_ref: T) -> T {
|
||||
tuple_ref
|
||||
}
|
||||
|
||||
fn reduce_tuple_init(&mut self, input: &TupleInitExpression<'a>, values: Vec<T>) -> T {
|
||||
T::default().append_all(values.into_iter())
|
||||
}
|
||||
|
||||
fn reduce_unary(&mut self, input: &UnaryExpression<'a>, inner: T) -> T {
|
||||
inner
|
||||
}
|
||||
|
||||
fn reduce_variable_ref(&mut self, input: &VariableRef<'a>) -> T {
|
||||
T::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub trait MonoidalReducerStatement<'a, T: Monoid>: MonoidalReducerExpression<'a, T> {
|
||||
fn reduce_statement(&mut self, input: &'a Statement<'a>, value: T) -> T {
|
||||
value
|
||||
}
|
||||
|
||||
// left = Some(ArrayIndex.0) always if AssignAccess::ArrayIndex. if member/tuple, always None
|
||||
fn reduce_assign_access(&mut self, input: &AssignAccess<'a>, left: Option<T>, right: Option<T>) -> T {
|
||||
left.unwrap_or_default().append_option(right)
|
||||
}
|
||||
|
||||
fn reduce_assign(&mut self, input: &AssignStatement<'a>, accesses: Vec<T>, value: T) -> T {
|
||||
T::default().append_all(accesses.into_iter()).append(value)
|
||||
}
|
||||
|
||||
fn reduce_block(&mut self, input: &BlockStatement<'a>, statements: Vec<T>) -> T {
|
||||
T::default().append_all(statements.into_iter())
|
||||
}
|
||||
|
||||
fn reduce_conditional_statement(
|
||||
&mut self,
|
||||
input: &ConditionalStatement<'a>,
|
||||
condition: T,
|
||||
if_true: T,
|
||||
if_false: Option<T>,
|
||||
) -> T {
|
||||
condition.append(if_true).append_option(if_false)
|
||||
}
|
||||
|
||||
fn reduce_formatted_string(&mut self, input: &FormatString<'a>, parameters: Vec<T>) -> T {
|
||||
T::default().append_all(parameters.into_iter())
|
||||
}
|
||||
|
||||
fn reduce_console(&mut self, input: &ConsoleStatement<'a>, argument: T) -> T {
|
||||
argument
|
||||
}
|
||||
|
||||
fn reduce_definition(&mut self, input: &DefinitionStatement<'a>, value: T) -> T {
|
||||
value
|
||||
}
|
||||
|
||||
fn reduce_expression_statement(&mut self, input: &ExpressionStatement<'a>, expression: T) -> T {
|
||||
expression
|
||||
}
|
||||
|
||||
fn reduce_iteration(&mut self, input: &IterationStatement<'a>, start: T, stop: T, body: T) -> T {
|
||||
start.append(stop).append(body)
|
||||
}
|
||||
|
||||
fn reduce_return(&mut self, input: &ReturnStatement<'a>, value: T) -> T {
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub trait MonoidalReducerProgram<'a, T: Monoid>: MonoidalReducerStatement<'a, T> {
|
||||
fn reduce_function(&mut self, input: &'a Function<'a>, body: T) -> T {
|
||||
body
|
||||
}
|
||||
|
||||
fn reduce_circuit_member(&mut self, input: &CircuitMember<'a>, function: Option<T>) -> T {
|
||||
function.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn reduce_circuit(&mut self, input: &'a Circuit<'a>, members: Vec<T>) -> T {
|
||||
T::default().append_all(members.into_iter())
|
||||
}
|
||||
|
||||
fn reduce_program(&mut self, input: &Program, imported_modules: Vec<T>, functions: Vec<T>, circuits: Vec<T>) -> T {
|
||||
T::default()
|
||||
.append_all(imported_modules.into_iter())
|
||||
.append_all(functions.into_iter())
|
||||
.append_all(circuits.into_iter())
|
||||
}
|
||||
}
|
357
asg/src/reducer/reconstructing_director.rs
Normal file
357
asg/src/reducer/reconstructing_director.rs
Normal file
@ -0,0 +1,357 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use super::*;
|
||||
use crate::{expression::*, program::*, statement::*, AsgContext};
|
||||
|
||||
/*
|
||||
reconstructing director tries to maintain a normalized ASG but may require renormalization under the following circumstances:
|
||||
* breaking strict reducer model (i.e. live mutations)
|
||||
* dropping or duplicating branches
|
||||
*/
|
||||
pub struct ReconstructingDirector<'a, R: ReconstructingReducerExpression<'a>> {
|
||||
context: AsgContext<'a>,
|
||||
reducer: R,
|
||||
}
|
||||
|
||||
impl<'a, R: ReconstructingReducerExpression<'a>> ReconstructingDirector<'a, R> {
|
||||
pub fn new(context: AsgContext<'a>, reducer: R) -> Self {
|
||||
Self { context, reducer }
|
||||
}
|
||||
|
||||
pub fn reducer(self) -> R {
|
||||
self.reducer
|
||||
}
|
||||
|
||||
pub fn reduce_expression(&mut self, input: &'a Expression<'a>) -> &'a Expression<'a> {
|
||||
let value = match input.clone() {
|
||||
Expression::ArrayAccess(e) => self.reduce_array_access(e),
|
||||
Expression::ArrayInit(e) => self.reduce_array_init(e),
|
||||
Expression::ArrayInline(e) => self.reduce_array_inline(e),
|
||||
Expression::ArrayRangeAccess(e) => self.reduce_array_range_access(e),
|
||||
Expression::Binary(e) => self.reduce_binary(e),
|
||||
Expression::Call(e) => self.reduce_call(e),
|
||||
Expression::CircuitAccess(e) => self.reduce_circuit_access(e),
|
||||
Expression::CircuitInit(e) => self.reduce_circuit_init(e),
|
||||
Expression::Ternary(e) => self.reduce_ternary_expression(e),
|
||||
Expression::Cast(e) => self.reduce_cast_expression(e),
|
||||
Expression::Constant(e) => self.reduce_constant(e),
|
||||
Expression::TupleAccess(e) => self.reduce_tuple_access(e),
|
||||
Expression::TupleInit(e) => self.reduce_tuple_init(e),
|
||||
Expression::Unary(e) => self.reduce_unary(e),
|
||||
Expression::VariableRef(e) => {
|
||||
{
|
||||
let mut variable = e.variable.borrow_mut();
|
||||
let index = variable.references.iter().position(|x| (*x).ptr_eq(input));
|
||||
if let Some(index) = index {
|
||||
variable.references.remove(index);
|
||||
}
|
||||
}
|
||||
self.reduce_variable_ref(e)
|
||||
}
|
||||
};
|
||||
|
||||
let allocated = self
|
||||
.context
|
||||
.alloc_expression(self.reducer.reduce_expression(input, value));
|
||||
if let Expression::VariableRef(reference) = allocated {
|
||||
let mut variable = reference.variable.borrow_mut();
|
||||
variable.references.push(allocated);
|
||||
}
|
||||
allocated
|
||||
}
|
||||
|
||||
pub fn reduce_array_access(&mut self, input: ArrayAccessExpression<'a>) -> Expression<'a> {
|
||||
let array = self.reduce_expression(input.array.get());
|
||||
let index = self.reduce_expression(input.index.get());
|
||||
|
||||
self.reducer.reduce_array_access(input, array, index)
|
||||
}
|
||||
|
||||
pub fn reduce_array_init(&mut self, input: ArrayInitExpression<'a>) -> Expression<'a> {
|
||||
let element = self.reduce_expression(input.element.get());
|
||||
|
||||
self.reducer.reduce_array_init(input, element)
|
||||
}
|
||||
|
||||
pub fn reduce_array_inline(&mut self, input: ArrayInlineExpression<'a>) -> Expression<'a> {
|
||||
let elements = input
|
||||
.elements
|
||||
.iter()
|
||||
.map(|(x, spread)| (self.reduce_expression(x.get()), *spread))
|
||||
.collect();
|
||||
|
||||
self.reducer.reduce_array_inline(input, elements)
|
||||
}
|
||||
|
||||
pub fn reduce_array_range_access(&mut self, input: ArrayRangeAccessExpression<'a>) -> Expression<'a> {
|
||||
let array = self.reduce_expression(input.array.get());
|
||||
let left = input.left.get().map(|e| self.reduce_expression(e));
|
||||
let right = input.right.get().map(|e| self.reduce_expression(e));
|
||||
|
||||
self.reducer.reduce_array_range_access(input, array, left, right)
|
||||
}
|
||||
|
||||
pub fn reduce_binary(&mut self, input: BinaryExpression<'a>) -> Expression<'a> {
|
||||
let left = self.reduce_expression(input.left.get());
|
||||
let right = self.reduce_expression(input.right.get());
|
||||
|
||||
self.reducer.reduce_binary(input, left, right)
|
||||
}
|
||||
|
||||
pub fn reduce_call(&mut self, input: CallExpression<'a>) -> Expression<'a> {
|
||||
let target = input.target.get().map(|e| self.reduce_expression(e));
|
||||
let arguments = input
|
||||
.arguments
|
||||
.iter()
|
||||
.map(|e| self.reduce_expression(e.get()))
|
||||
.collect();
|
||||
|
||||
self.reducer.reduce_call(input, target, arguments)
|
||||
}
|
||||
|
||||
pub fn reduce_circuit_access(&mut self, input: CircuitAccessExpression<'a>) -> Expression<'a> {
|
||||
let target = input.target.get().map(|e| self.reduce_expression(e));
|
||||
|
||||
self.reducer.reduce_circuit_access(input, target)
|
||||
}
|
||||
|
||||
pub fn reduce_circuit_init(&mut self, input: CircuitInitExpression<'a>) -> Expression<'a> {
|
||||
let values = input
|
||||
.values
|
||||
.iter()
|
||||
.map(|(ident, e)| (ident.clone(), self.reduce_expression(e.get())))
|
||||
.collect();
|
||||
|
||||
self.reducer.reduce_circuit_init(input, values)
|
||||
}
|
||||
|
||||
pub fn reduce_ternary_expression(&mut self, input: TernaryExpression<'a>) -> Expression<'a> {
|
||||
let condition = self.reduce_expression(input.condition.get());
|
||||
let if_true = self.reduce_expression(input.if_true.get());
|
||||
let if_false = self.reduce_expression(input.if_false.get());
|
||||
|
||||
self.reducer
|
||||
.reduce_ternary_expression(input, condition, if_true, if_false)
|
||||
}
|
||||
|
||||
pub fn reduce_cast_expression(&mut self, input: CastExpression<'a>) -> Expression<'a> {
|
||||
let inner = self.reduce_expression(input.inner.get());
|
||||
|
||||
self.reducer.reduce_cast_expression(input, inner)
|
||||
}
|
||||
|
||||
pub fn reduce_constant(&mut self, input: Constant<'a>) -> Expression<'a> {
|
||||
self.reducer.reduce_constant(input)
|
||||
}
|
||||
|
||||
pub fn reduce_tuple_access(&mut self, input: TupleAccessExpression<'a>) -> Expression<'a> {
|
||||
let tuple_ref = self.reduce_expression(input.tuple_ref.get());
|
||||
|
||||
self.reducer.reduce_tuple_access(input, tuple_ref)
|
||||
}
|
||||
|
||||
pub fn reduce_tuple_init(&mut self, input: TupleInitExpression<'a>) -> Expression<'a> {
|
||||
let values = input.elements.iter().map(|e| self.reduce_expression(e.get())).collect();
|
||||
|
||||
self.reducer.reduce_tuple_init(input, values)
|
||||
}
|
||||
|
||||
pub fn reduce_unary(&mut self, input: UnaryExpression<'a>) -> Expression<'a> {
|
||||
let inner = self.reduce_expression(input.inner.get());
|
||||
|
||||
self.reducer.reduce_unary(input, inner)
|
||||
}
|
||||
|
||||
pub fn reduce_variable_ref(&mut self, input: VariableRef<'a>) -> Expression<'a> {
|
||||
self.reducer.reduce_variable_ref(input)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R: ReconstructingReducerStatement<'a>> ReconstructingDirector<'a, R> {
|
||||
pub fn reduce_statement(&mut self, input: &'a Statement<'a>) -> &'a Statement<'a> {
|
||||
let value = match input.clone() {
|
||||
Statement::Assign(s) => self.reduce_assign(s),
|
||||
Statement::Block(s) => self.reduce_block(s),
|
||||
Statement::Conditional(s) => self.reduce_conditional_statement(s),
|
||||
Statement::Console(s) => self.reduce_console(s),
|
||||
Statement::Definition(s) => self.reduce_definition(s),
|
||||
Statement::Expression(s) => self.reduce_expression_statement(s),
|
||||
Statement::Iteration(s) => self.reduce_iteration(s),
|
||||
Statement::Return(s) => self.reduce_return(s),
|
||||
x @ Statement::Empty(_) => x,
|
||||
};
|
||||
|
||||
self.reducer.reduce_statement_alloc(self.context, input, value)
|
||||
}
|
||||
|
||||
pub fn reduce_assign_access(&mut self, input: AssignAccess<'a>) -> AssignAccess<'a> {
|
||||
match &input {
|
||||
AssignAccess::ArrayRange(left, right) => {
|
||||
let left = left.get().map(|e| self.reduce_expression(e));
|
||||
let right = right.get().map(|e| self.reduce_expression(e));
|
||||
self.reducer.reduce_assign_access_range(input, left, right)
|
||||
}
|
||||
AssignAccess::ArrayIndex(index) => {
|
||||
let index = self.reduce_expression(index.get());
|
||||
self.reducer.reduce_assign_access_index(input, index)
|
||||
}
|
||||
_ => self.reducer.reduce_assign_access(input),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reduce_assign(&mut self, input: AssignStatement<'a>) -> Statement<'a> {
|
||||
let accesses = input
|
||||
.target_accesses
|
||||
.iter()
|
||||
.map(|x| self.reduce_assign_access(x.clone()))
|
||||
.collect();
|
||||
let value = self.reduce_expression(input.value.get());
|
||||
|
||||
self.reducer.reduce_assign(input, accesses, value)
|
||||
}
|
||||
|
||||
pub fn reduce_block(&mut self, input: BlockStatement<'a>) -> Statement<'a> {
|
||||
let statements = input
|
||||
.statements
|
||||
.iter()
|
||||
.map(|x| self.reduce_statement(x.get()))
|
||||
.collect();
|
||||
|
||||
self.reducer.reduce_block(input, statements)
|
||||
}
|
||||
|
||||
pub fn reduce_conditional_statement(&mut self, input: ConditionalStatement<'a>) -> Statement<'a> {
|
||||
let condition = self.reduce_expression(input.condition.get());
|
||||
let if_true = self.reduce_statement(input.result.get());
|
||||
let if_false = input.next.get().map(|s| self.reduce_statement(s));
|
||||
|
||||
self.reducer
|
||||
.reduce_conditional_statement(input, condition, if_true, if_false)
|
||||
}
|
||||
|
||||
pub fn reduce_formatted_string(&mut self, input: FormatString<'a>) -> FormatString<'a> {
|
||||
let parameters = input
|
||||
.parameters
|
||||
.iter()
|
||||
.map(|e| self.reduce_expression(e.get()))
|
||||
.collect();
|
||||
|
||||
self.reducer.reduce_formatted_string(input, parameters)
|
||||
}
|
||||
|
||||
pub fn reduce_console(&mut self, input: ConsoleStatement<'a>) -> Statement<'a> {
|
||||
match &input.function {
|
||||
ConsoleFunction::Assert(argument) => {
|
||||
let argument = self.reduce_expression(argument.get());
|
||||
self.reducer.reduce_console_assert(input, argument)
|
||||
}
|
||||
ConsoleFunction::Debug(f) | ConsoleFunction::Error(f) | ConsoleFunction::Log(f) => {
|
||||
let formatted = self.reduce_formatted_string(f.clone());
|
||||
self.reducer.reduce_console_log(input, formatted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reduce_definition(&mut self, input: DefinitionStatement<'a>) -> Statement<'a> {
|
||||
let value = self.reduce_expression(input.value.get());
|
||||
|
||||
self.reducer.reduce_definition(input, value)
|
||||
}
|
||||
|
||||
pub fn reduce_expression_statement(&mut self, input: ExpressionStatement<'a>) -> Statement<'a> {
|
||||
let value = self.reduce_expression(input.expression.get());
|
||||
|
||||
self.reducer.reduce_expression_statement(input, value)
|
||||
}
|
||||
|
||||
pub fn reduce_iteration(&mut self, input: IterationStatement<'a>) -> Statement<'a> {
|
||||
let start = self.reduce_expression(input.start.get());
|
||||
let stop = self.reduce_expression(input.stop.get());
|
||||
let body = self.reduce_statement(input.body.get());
|
||||
|
||||
self.reducer.reduce_iteration(input, start, stop, body)
|
||||
}
|
||||
|
||||
pub fn reduce_return(&mut self, input: ReturnStatement<'a>) -> Statement<'a> {
|
||||
let value = self.reduce_expression(input.expression.get());
|
||||
|
||||
self.reducer.reduce_return(input, value)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl<'a, R: ReconstructingReducerProgram<'a>> ReconstructingDirector<'a, R> {
|
||||
fn reduce_function(&mut self, input: &'a Function<'a>) -> &'a Function<'a> {
|
||||
let body = input.body.get().map(|s| self.reduce_statement(s));
|
||||
|
||||
self.reducer.reduce_function(input, body)
|
||||
}
|
||||
|
||||
pub fn reduce_circuit_member(&mut self, input: CircuitMember<'a>) -> CircuitMember<'a> {
|
||||
match input {
|
||||
CircuitMember::Function(function) => {
|
||||
let function = self.reduce_function(function);
|
||||
self.reducer.reduce_circuit_member_function(input, function)
|
||||
}
|
||||
CircuitMember::Variable(_) => self.reducer.reduce_circuit_member_variable(input),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reduce_circuit(&mut self, input: &'a Circuit<'a>) -> &'a Circuit<'a> {
|
||||
let members = input
|
||||
.members
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|(_, member)| self.reduce_circuit_member(member.clone()))
|
||||
.collect();
|
||||
|
||||
self.reducer.reduce_circuit(input, members)
|
||||
}
|
||||
|
||||
pub fn reduce_global_const(&mut self, input: &'a DefinitionStatement<'a>) -> &'a DefinitionStatement<'a> {
|
||||
let value = self.reduce_expression(input.value.get());
|
||||
|
||||
self.reducer.reduce_global_const(input, value)
|
||||
}
|
||||
|
||||
pub fn reduce_program(&mut self, input: Program<'a>) -> Program<'a> {
|
||||
let imported_modules = input
|
||||
.imported_modules
|
||||
.iter()
|
||||
.map(|(module, import)| (module.clone(), self.reduce_program(import.clone())))
|
||||
.collect();
|
||||
let functions = input
|
||||
.functions
|
||||
.iter()
|
||||
.map(|(name, f)| (name.clone(), self.reduce_function(f)))
|
||||
.collect();
|
||||
let circuits = input
|
||||
.circuits
|
||||
.iter()
|
||||
.map(|(name, c)| (name.clone(), self.reduce_circuit(c)))
|
||||
.collect();
|
||||
|
||||
let global_consts = input
|
||||
.global_consts
|
||||
.iter()
|
||||
.map(|(name, gc)| (name.clone(), self.reduce_global_const(gc)))
|
||||
.collect();
|
||||
|
||||
self.reducer
|
||||
.reduce_program(input, imported_modules, functions, circuits, global_consts)
|
||||
}
|
||||
}
|
414
asg/src/reducer/reconstructing_reducer.rs
Normal file
414
asg/src/reducer/reconstructing_reducer.rs
Normal file
@ -0,0 +1,414 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
use leo_ast::Identifier;
|
||||
|
||||
use crate::{expression::*, program::*, statement::*, AsgContext};
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub trait ReconstructingReducerExpression<'a> {
|
||||
fn reduce_expression(&mut self, input: &'a Expression<'a>, value: Expression<'a>) -> Expression<'a> {
|
||||
value
|
||||
}
|
||||
|
||||
fn reduce_array_access(
|
||||
&mut self,
|
||||
input: ArrayAccessExpression<'a>,
|
||||
array: &'a Expression<'a>,
|
||||
index: &'a Expression<'a>,
|
||||
) -> Expression<'a> {
|
||||
Expression::ArrayAccess(ArrayAccessExpression {
|
||||
parent: input.parent,
|
||||
array: Cell::new(array),
|
||||
index: Cell::new(index),
|
||||
span: input.span,
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_array_init(&mut self, input: ArrayInitExpression<'a>, element: &'a Expression<'a>) -> Expression<'a> {
|
||||
Expression::ArrayInit(ArrayInitExpression {
|
||||
parent: input.parent,
|
||||
element: Cell::new(element),
|
||||
len: input.len,
|
||||
span: input.span,
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_array_inline(
|
||||
&mut self,
|
||||
input: ArrayInlineExpression<'a>,
|
||||
elements: Vec<(&'a Expression<'a>, bool)>,
|
||||
) -> Expression<'a> {
|
||||
Expression::ArrayInline(ArrayInlineExpression {
|
||||
parent: input.parent,
|
||||
elements: elements.into_iter().map(|x| (Cell::new(x.0), x.1)).collect(),
|
||||
span: input.span,
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_array_range_access(
|
||||
&mut self,
|
||||
input: ArrayRangeAccessExpression<'a>,
|
||||
array: &'a Expression<'a>,
|
||||
left: Option<&'a Expression<'a>>,
|
||||
right: Option<&'a Expression<'a>>,
|
||||
) -> Expression<'a> {
|
||||
Expression::ArrayRangeAccess(ArrayRangeAccessExpression {
|
||||
parent: input.parent,
|
||||
array: Cell::new(array),
|
||||
left: Cell::new(left),
|
||||
right: Cell::new(right),
|
||||
span: input.span,
|
||||
length: input.length,
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_binary(
|
||||
&mut self,
|
||||
input: BinaryExpression<'a>,
|
||||
left: &'a Expression<'a>,
|
||||
right: &'a Expression<'a>,
|
||||
) -> Expression<'a> {
|
||||
Expression::Binary(BinaryExpression {
|
||||
parent: input.parent,
|
||||
left: Cell::new(left),
|
||||
right: Cell::new(right),
|
||||
span: input.span,
|
||||
operation: input.operation,
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_call(
|
||||
&mut self,
|
||||
input: CallExpression<'a>,
|
||||
target: Option<&'a Expression<'a>>,
|
||||
arguments: Vec<&'a Expression<'a>>,
|
||||
) -> Expression<'a> {
|
||||
Expression::Call(CallExpression {
|
||||
parent: input.parent,
|
||||
function: input.function,
|
||||
target: Cell::new(target),
|
||||
arguments: arguments.into_iter().map(Cell::new).collect(),
|
||||
span: input.span,
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_circuit_access(
|
||||
&mut self,
|
||||
input: CircuitAccessExpression<'a>,
|
||||
target: Option<&'a Expression<'a>>,
|
||||
) -> Expression<'a> {
|
||||
Expression::CircuitAccess(CircuitAccessExpression {
|
||||
parent: input.parent,
|
||||
circuit: input.circuit,
|
||||
target: Cell::new(target),
|
||||
member: input.member,
|
||||
span: input.span,
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_circuit_init(
|
||||
&mut self,
|
||||
input: CircuitInitExpression<'a>,
|
||||
values: Vec<(Identifier, &'a Expression<'a>)>,
|
||||
) -> Expression<'a> {
|
||||
Expression::CircuitInit(CircuitInitExpression {
|
||||
parent: input.parent,
|
||||
circuit: input.circuit,
|
||||
values: values.into_iter().map(|x| (x.0, Cell::new(x.1))).collect(),
|
||||
span: input.span,
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_ternary_expression(
|
||||
&mut self,
|
||||
input: TernaryExpression<'a>,
|
||||
condition: &'a Expression<'a>,
|
||||
if_true: &'a Expression<'a>,
|
||||
if_false: &'a Expression<'a>,
|
||||
) -> Expression<'a> {
|
||||
Expression::Ternary(TernaryExpression {
|
||||
parent: input.parent,
|
||||
condition: Cell::new(condition),
|
||||
if_true: Cell::new(if_true),
|
||||
if_false: Cell::new(if_false),
|
||||
span: input.span,
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_cast_expression(&mut self, input: CastExpression<'a>, inner: &'a Expression<'a>) -> Expression<'a> {
|
||||
Expression::Cast(CastExpression {
|
||||
parent: input.parent,
|
||||
inner: Cell::new(inner),
|
||||
target_type: input.target_type,
|
||||
span: input.span,
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_constant(&mut self, input: Constant<'a>) -> Expression<'a> {
|
||||
Expression::Constant(input)
|
||||
}
|
||||
|
||||
fn reduce_tuple_access(
|
||||
&mut self,
|
||||
input: TupleAccessExpression<'a>,
|
||||
tuple_ref: &'a Expression<'a>,
|
||||
) -> Expression<'a> {
|
||||
Expression::TupleAccess(TupleAccessExpression {
|
||||
parent: input.parent,
|
||||
tuple_ref: Cell::new(tuple_ref),
|
||||
index: input.index,
|
||||
span: input.span,
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_tuple_init(&mut self, input: TupleInitExpression<'a>, values: Vec<&'a Expression<'a>>) -> Expression<'a> {
|
||||
Expression::TupleInit(TupleInitExpression {
|
||||
parent: input.parent,
|
||||
elements: values.into_iter().map(Cell::new).collect(),
|
||||
span: input.span,
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_unary(&mut self, input: UnaryExpression<'a>, inner: &'a Expression<'a>) -> Expression<'a> {
|
||||
Expression::Unary(UnaryExpression {
|
||||
parent: input.parent,
|
||||
inner: Cell::new(inner),
|
||||
span: input.span,
|
||||
operation: input.operation,
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_variable_ref(&mut self, input: VariableRef<'a>) -> Expression<'a> {
|
||||
Expression::VariableRef(input)
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub trait ReconstructingReducerStatement<'a>: ReconstructingReducerExpression<'a> {
|
||||
fn reduce_statement_alloc(
|
||||
&mut self,
|
||||
context: AsgContext<'a>,
|
||||
input: &'a Statement<'a>,
|
||||
value: Statement<'a>,
|
||||
) -> &'a Statement<'a> {
|
||||
context.alloc_statement(value)
|
||||
}
|
||||
|
||||
fn reduce_statement(&mut self, input: &'a Statement<'a>, value: Statement<'a>) -> Statement<'a> {
|
||||
value
|
||||
}
|
||||
|
||||
fn reduce_assign_access_range(
|
||||
&mut self,
|
||||
input: AssignAccess<'a>,
|
||||
left: Option<&'a Expression<'a>>,
|
||||
right: Option<&'a Expression<'a>>,
|
||||
) -> AssignAccess<'a> {
|
||||
AssignAccess::ArrayRange(Cell::new(left), Cell::new(right))
|
||||
}
|
||||
|
||||
fn reduce_assign_access_index(&mut self, input: AssignAccess<'a>, index: &'a Expression<'a>) -> AssignAccess<'a> {
|
||||
AssignAccess::ArrayIndex(Cell::new(index))
|
||||
}
|
||||
|
||||
fn reduce_assign_access(&mut self, input: AssignAccess<'a>) -> AssignAccess<'a> {
|
||||
input
|
||||
}
|
||||
|
||||
fn reduce_assign(
|
||||
&mut self,
|
||||
input: AssignStatement<'a>,
|
||||
accesses: Vec<AssignAccess<'a>>,
|
||||
value: &'a Expression<'a>,
|
||||
) -> Statement<'a> {
|
||||
Statement::Assign(AssignStatement {
|
||||
parent: input.parent,
|
||||
span: input.span,
|
||||
operation: input.operation,
|
||||
target_accesses: accesses,
|
||||
target_variable: input.target_variable,
|
||||
value: Cell::new(value),
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_block(&mut self, input: BlockStatement<'a>, statements: Vec<&'a Statement<'a>>) -> Statement<'a> {
|
||||
Statement::Block(BlockStatement {
|
||||
parent: input.parent,
|
||||
span: input.span,
|
||||
statements: statements.into_iter().map(Cell::new).collect(),
|
||||
scope: input.scope,
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_conditional_statement(
|
||||
&mut self,
|
||||
input: ConditionalStatement<'a>,
|
||||
condition: &'a Expression<'a>,
|
||||
if_true: &'a Statement<'a>,
|
||||
if_false: Option<&'a Statement<'a>>,
|
||||
) -> Statement<'a> {
|
||||
Statement::Conditional(ConditionalStatement {
|
||||
parent: input.parent,
|
||||
span: input.span,
|
||||
condition: Cell::new(condition),
|
||||
result: Cell::new(if_true),
|
||||
next: Cell::new(if_false),
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_formatted_string(
|
||||
&mut self,
|
||||
input: FormatString<'a>,
|
||||
parameters: Vec<&'a Expression<'a>>,
|
||||
) -> FormatString<'a> {
|
||||
FormatString {
|
||||
span: input.span,
|
||||
parts: input.parts,
|
||||
parameters: parameters.into_iter().map(Cell::new).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn reduce_console_assert(&mut self, input: ConsoleStatement<'a>, argument: &'a Expression<'a>) -> Statement<'a> {
|
||||
assert!(matches!(input.function, ConsoleFunction::Assert(_)));
|
||||
Statement::Console(ConsoleStatement {
|
||||
parent: input.parent,
|
||||
span: input.span,
|
||||
function: ConsoleFunction::Assert(Cell::new(argument)),
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_console_log(&mut self, input: ConsoleStatement<'a>, argument: FormatString<'a>) -> Statement<'a> {
|
||||
assert!(!matches!(input.function, ConsoleFunction::Assert(_)));
|
||||
Statement::Console(ConsoleStatement {
|
||||
parent: input.parent,
|
||||
span: input.span,
|
||||
function: match input.function {
|
||||
ConsoleFunction::Assert(_) => unimplemented!(),
|
||||
ConsoleFunction::Debug(_) => ConsoleFunction::Debug(argument),
|
||||
ConsoleFunction::Error(_) => ConsoleFunction::Error(argument),
|
||||
ConsoleFunction::Log(_) => ConsoleFunction::Log(argument),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_definition(&mut self, input: DefinitionStatement<'a>, value: &'a Expression<'a>) -> Statement<'a> {
|
||||
Statement::Definition(DefinitionStatement {
|
||||
parent: input.parent,
|
||||
span: input.span,
|
||||
variables: input.variables,
|
||||
value: Cell::new(value),
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_expression_statement(
|
||||
&mut self,
|
||||
input: ExpressionStatement<'a>,
|
||||
expression: &'a Expression<'a>,
|
||||
) -> Statement<'a> {
|
||||
Statement::Expression(ExpressionStatement {
|
||||
parent: input.parent,
|
||||
span: input.span,
|
||||
expression: Cell::new(expression),
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_iteration(
|
||||
&mut self,
|
||||
input: IterationStatement<'a>,
|
||||
start: &'a Expression<'a>,
|
||||
stop: &'a Expression<'a>,
|
||||
body: &'a Statement<'a>,
|
||||
) -> Statement<'a> {
|
||||
Statement::Iteration(IterationStatement {
|
||||
parent: input.parent,
|
||||
span: input.span,
|
||||
variable: input.variable,
|
||||
start: Cell::new(start),
|
||||
stop: Cell::new(stop),
|
||||
body: Cell::new(body),
|
||||
})
|
||||
}
|
||||
|
||||
fn reduce_return(&mut self, input: ReturnStatement<'a>, value: &'a Expression<'a>) -> Statement<'a> {
|
||||
Statement::Return(ReturnStatement {
|
||||
parent: input.parent,
|
||||
span: input.span,
|
||||
expression: Cell::new(value),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub trait ReconstructingReducerProgram<'a>: ReconstructingReducerStatement<'a> {
|
||||
// todo @protryon: this is kind of hacky
|
||||
fn reduce_function(&mut self, input: &'a Function<'a>, body: Option<&'a Statement<'a>>) -> &'a Function<'a> {
|
||||
input.body.set(body);
|
||||
input
|
||||
}
|
||||
|
||||
fn reduce_circuit_member_variable(&mut self, input: CircuitMember<'a>) -> CircuitMember<'a> {
|
||||
input
|
||||
}
|
||||
|
||||
fn reduce_circuit_member_function(
|
||||
&mut self,
|
||||
input: CircuitMember<'a>,
|
||||
function: &'a Function<'a>,
|
||||
) -> CircuitMember<'a> {
|
||||
CircuitMember::Function(function)
|
||||
}
|
||||
|
||||
// todo @protryon: this is kind of hacky
|
||||
fn reduce_circuit(&mut self, input: &'a Circuit<'a>, members: Vec<CircuitMember<'a>>) -> &'a Circuit<'a> {
|
||||
let mut input_members = input.members.borrow_mut();
|
||||
for ((name, input_member), member) in input_members.iter_mut().zip(members) {
|
||||
*input_member = member;
|
||||
}
|
||||
input
|
||||
}
|
||||
|
||||
fn reduce_global_const(
|
||||
&mut self,
|
||||
input: &'a DefinitionStatement<'a>,
|
||||
value: &'a Expression<'a>,
|
||||
) -> &'a DefinitionStatement<'a> {
|
||||
input.value.set(value);
|
||||
input
|
||||
}
|
||||
|
||||
fn reduce_program(
|
||||
&mut self,
|
||||
input: Program<'a>,
|
||||
imported_modules: Vec<(String, Program<'a>)>,
|
||||
functions: Vec<(String, &'a Function<'a>)>,
|
||||
circuits: Vec<(String, &'a Circuit<'a>)>,
|
||||
global_consts: Vec<(String, &'a DefinitionStatement<'a>)>,
|
||||
) -> Program<'a> {
|
||||
Program {
|
||||
context: input.context,
|
||||
id: input.id,
|
||||
name: input.name,
|
||||
imported_modules: imported_modules.into_iter().collect(),
|
||||
functions: functions.into_iter().collect(),
|
||||
circuits: circuits.into_iter().collect(),
|
||||
scope: input.scope,
|
||||
global_consts: global_consts.into_iter().collect(),
|
||||
}
|
||||
}
|
||||
}
|
169
asg/src/reducer/visitor.rs
Normal file
169
asg/src/reducer/visitor.rs
Normal file
@ -0,0 +1,169 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
use crate::{expression::*, program::*, statement::*};
|
||||
|
||||
pub enum VisitResult {
|
||||
VisitChildren,
|
||||
SkipChildren,
|
||||
Exit,
|
||||
}
|
||||
|
||||
impl Default for VisitResult {
|
||||
fn default() -> Self {
|
||||
VisitResult::VisitChildren
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub trait ExpressionVisitor<'a> {
|
||||
fn visit_expression(&mut self, input: &Cell<&'a Expression<'a>>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_array_access(&mut self, input: &ArrayAccessExpression<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_array_init(&mut self, input: &ArrayInitExpression<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_array_inline(&mut self, input: &ArrayInlineExpression<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_array_range_access(&mut self, input: &ArrayRangeAccessExpression<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_binary(&mut self, input: &BinaryExpression<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_call(&mut self, input: &CallExpression<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_circuit_access(&mut self, input: &CircuitAccessExpression<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_circuit_init(&mut self, input: &CircuitInitExpression<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_ternary_expression(&mut self, input: &TernaryExpression<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_cast_expression(&mut self, input: &CastExpression<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_constant(&mut self, input: &Constant<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_tuple_access(&mut self, input: &TupleAccessExpression<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_tuple_init(&mut self, input: &TupleInitExpression<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_unary(&mut self, input: &UnaryExpression<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_variable_ref(&mut self, input: &VariableRef<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub trait StatementVisitor<'a>: ExpressionVisitor<'a> {
|
||||
fn visit_statement(&mut self, input: &Cell<&'a Statement<'a>>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
// left = Some(ArrayIndex.0) always if AssignAccess::ArrayIndex. if member/tuple, always None
|
||||
fn visit_assign_access(&mut self, input: &AssignAccess<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_assign(&mut self, input: &AssignStatement<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_block(&mut self, input: &BlockStatement<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_conditional_statement(&mut self, input: &ConditionalStatement<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_formatted_string(&mut self, input: &FormatString<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_console(&mut self, input: &ConsoleStatement<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_definition(&mut self, input: &DefinitionStatement<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_expression_statement(&mut self, input: &ExpressionStatement<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_iteration(&mut self, input: &IterationStatement<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_return(&mut self, input: &ReturnStatement<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub trait ProgramVisitor<'a>: StatementVisitor<'a> {
|
||||
fn visit_function(&mut self, input: &'a Function<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_circuit_member(&mut self, input: &CircuitMember<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_circuit(&mut self, input: &'a Circuit<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_global_const(&mut self, input: &'a DefinitionStatement<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn visit_program(&mut self, input: &Program<'a>) -> VisitResult {
|
||||
Default::default()
|
||||
}
|
||||
}
|
457
asg/src/reducer/visitor_director.rs
Normal file
457
asg/src/reducer/visitor_director.rs
Normal file
@ -0,0 +1,457 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use super::*;
|
||||
use crate::{expression::*, program::*, statement::*};
|
||||
|
||||
use std::{cell::Cell, marker::PhantomData};
|
||||
|
||||
pub struct VisitorDirector<'a, R: ExpressionVisitor<'a>> {
|
||||
visitor: R,
|
||||
lifetime: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
pub type ConcreteVisitResult = Result<(), ()>;
|
||||
|
||||
impl Into<ConcreteVisitResult> for VisitResult {
|
||||
fn into(self) -> ConcreteVisitResult {
|
||||
match self {
|
||||
VisitResult::VisitChildren => Ok(()),
|
||||
VisitResult::SkipChildren => Ok(()),
|
||||
VisitResult::Exit => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R: ExpressionVisitor<'a>> VisitorDirector<'a, R> {
|
||||
pub fn new(visitor: R) -> Self {
|
||||
Self {
|
||||
visitor,
|
||||
lifetime: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visitor(self) -> R {
|
||||
self.visitor
|
||||
}
|
||||
|
||||
pub fn visit_expression(&mut self, input: &Cell<&'a Expression<'a>>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_expression(input) {
|
||||
VisitResult::VisitChildren => match input.get() {
|
||||
Expression::ArrayAccess(e) => self.visit_array_access(e),
|
||||
Expression::ArrayInit(e) => self.visit_array_init(e),
|
||||
Expression::ArrayInline(e) => self.visit_array_inline(e),
|
||||
Expression::ArrayRangeAccess(e) => self.visit_array_range_access(e),
|
||||
Expression::Binary(e) => self.visit_binary(e),
|
||||
Expression::Call(e) => self.visit_call(e),
|
||||
Expression::CircuitAccess(e) => self.visit_circuit_access(e),
|
||||
Expression::CircuitInit(e) => self.visit_circuit_init(e),
|
||||
Expression::Ternary(e) => self.visit_ternary_expression(e),
|
||||
Expression::Cast(e) => self.visit_cast_expression(e),
|
||||
Expression::Constant(e) => self.visit_constant(e),
|
||||
Expression::TupleAccess(e) => self.visit_tuple_access(e),
|
||||
Expression::TupleInit(e) => self.visit_tuple_init(e),
|
||||
Expression::Unary(e) => self.visit_unary(e),
|
||||
Expression::VariableRef(e) => self.visit_variable_ref(e),
|
||||
},
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_opt_expression(&mut self, input: &Cell<Option<&'a Expression<'a>>>) -> ConcreteVisitResult {
|
||||
let interior = input.get().map(Cell::new);
|
||||
if let Some(interior) = interior.as_ref() {
|
||||
let result = self.visit_expression(interior);
|
||||
input.replace(Some(interior.get()));
|
||||
result
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_array_access(&mut self, input: &ArrayAccessExpression<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_array_access(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
self.visit_expression(&input.array)?;
|
||||
self.visit_expression(&input.index)?;
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_array_init(&mut self, input: &ArrayInitExpression<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_array_init(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
self.visit_expression(&input.element)?;
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_array_inline(&mut self, input: &ArrayInlineExpression<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_array_inline(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
for (element, _) in input.elements.iter() {
|
||||
self.visit_expression(element)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_array_range_access(&mut self, input: &ArrayRangeAccessExpression<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_array_range_access(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
self.visit_expression(&input.array)?;
|
||||
self.visit_opt_expression(&input.left)?;
|
||||
self.visit_opt_expression(&input.right)?;
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_binary(&mut self, input: &BinaryExpression<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_binary(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
self.visit_expression(&input.left)?;
|
||||
self.visit_expression(&input.right)?;
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_call(&mut self, input: &CallExpression<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_call(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
self.visit_opt_expression(&input.target)?;
|
||||
for argument in input.arguments.iter() {
|
||||
self.visit_expression(argument)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_circuit_access(&mut self, input: &CircuitAccessExpression<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_circuit_access(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
self.visit_opt_expression(&input.target)?;
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_circuit_init(&mut self, input: &CircuitInitExpression<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_circuit_init(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
for (_, argument) in input.values.iter() {
|
||||
self.visit_expression(argument)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_ternary_expression(&mut self, input: &TernaryExpression<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_ternary_expression(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
self.visit_expression(&input.condition)?;
|
||||
self.visit_expression(&input.if_true)?;
|
||||
self.visit_expression(&input.if_false)?;
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_cast_expression(&mut self, input: &CastExpression<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_cast_expression(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
self.visit_expression(&input.inner)?;
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_constant(&mut self, input: &Constant<'a>) -> ConcreteVisitResult {
|
||||
self.visitor.visit_constant(input).into()
|
||||
}
|
||||
|
||||
pub fn visit_tuple_access(&mut self, input: &TupleAccessExpression<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_tuple_access(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
self.visit_expression(&input.tuple_ref)?;
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_tuple_init(&mut self, input: &TupleInitExpression<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_tuple_init(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
for argument in input.elements.iter() {
|
||||
self.visit_expression(argument)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_unary(&mut self, input: &UnaryExpression<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_unary(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
self.visit_expression(&input.inner)?;
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_variable_ref(&mut self, input: &VariableRef<'a>) -> ConcreteVisitResult {
|
||||
self.visitor.visit_variable_ref(input).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R: StatementVisitor<'a>> VisitorDirector<'a, R> {
|
||||
pub fn visit_statement(&mut self, input: &Cell<&'a Statement<'a>>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_statement(input) {
|
||||
VisitResult::VisitChildren => match input.get() {
|
||||
Statement::Assign(s) => self.visit_assign(s),
|
||||
Statement::Block(s) => self.visit_block(s),
|
||||
Statement::Conditional(s) => self.visit_conditional_statement(s),
|
||||
Statement::Console(s) => self.visit_console(s),
|
||||
Statement::Definition(s) => self.visit_definition(s),
|
||||
Statement::Expression(s) => self.visit_expression_statement(s),
|
||||
Statement::Iteration(s) => self.visit_iteration(s),
|
||||
Statement::Return(s) => self.visit_return(s),
|
||||
Statement::Empty(_) => Ok(()),
|
||||
},
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_opt_statement(&mut self, input: &Cell<Option<&'a Statement<'a>>>) -> ConcreteVisitResult {
|
||||
let interior = input.get().map(Cell::new);
|
||||
if let Some(interior) = interior.as_ref() {
|
||||
let result = self.visit_statement(interior);
|
||||
input.replace(Some(interior.get()));
|
||||
result
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_assign_access(&mut self, input: &AssignAccess<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_assign_access(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
match input {
|
||||
AssignAccess::ArrayRange(left, right) => {
|
||||
self.visit_opt_expression(left)?;
|
||||
self.visit_opt_expression(right)?;
|
||||
}
|
||||
AssignAccess::ArrayIndex(index) => self.visit_expression(index)?,
|
||||
_ => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_assign(&mut self, input: &AssignStatement<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_assign(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
for access in input.target_accesses.iter() {
|
||||
self.visit_assign_access(access)?;
|
||||
}
|
||||
self.visit_expression(&input.value)?;
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_block(&mut self, input: &BlockStatement<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_block(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
for statement in input.statements.iter() {
|
||||
self.visit_statement(statement)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_conditional_statement(&mut self, input: &ConditionalStatement<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_conditional_statement(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
self.visit_expression(&input.condition)?;
|
||||
self.visit_statement(&input.result)?;
|
||||
self.visit_opt_statement(&input.next)?;
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_formatted_string(&mut self, input: &FormatString<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_formatted_string(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
for parameter in input.parameters.iter() {
|
||||
self.visit_expression(parameter)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_console(&mut self, input: &ConsoleStatement<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_console(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
match &input.function {
|
||||
ConsoleFunction::Assert(e) => self.visit_expression(e)?,
|
||||
ConsoleFunction::Debug(f) | ConsoleFunction::Error(f) | ConsoleFunction::Log(f) => {
|
||||
self.visit_formatted_string(f)?
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_definition(&mut self, input: &DefinitionStatement<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_definition(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
self.visit_expression(&input.value)?;
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_expression_statement(&mut self, input: &ExpressionStatement<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_expression_statement(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
self.visit_expression(&input.expression)?;
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_iteration(&mut self, input: &IterationStatement<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_iteration(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
self.visit_expression(&input.start)?;
|
||||
self.visit_expression(&input.stop)?;
|
||||
self.visit_statement(&input.body)?;
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_return(&mut self, input: &ReturnStatement<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_return(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
self.visit_expression(&input.expression)?;
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R: ProgramVisitor<'a>> VisitorDirector<'a, R> {
|
||||
pub fn visit_function(&mut self, input: &'a Function<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_function(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
self.visit_opt_statement(&input.body)?;
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_circuit_member(&mut self, input: &CircuitMember<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_circuit_member(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
if let CircuitMember::Function(f) = input {
|
||||
self.visit_function(f)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_circuit(&mut self, input: &'a Circuit<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_circuit(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
for (_, member) in input.members.borrow().iter() {
|
||||
self.visit_circuit_member(member)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_global_const(&mut self, input: &'a DefinitionStatement<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_global_const(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
self.visit_expression(&input.value)?;
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn visit_program(&mut self, input: &Program<'a>) -> ConcreteVisitResult {
|
||||
match self.visitor.visit_program(input) {
|
||||
VisitResult::VisitChildren => {
|
||||
for (_, import) in input.imported_modules.iter() {
|
||||
self.visit_program(import)?;
|
||||
}
|
||||
for (_, function) in input.functions.iter() {
|
||||
self.visit_function(function)?;
|
||||
}
|
||||
for (_, circuit) in input.circuits.iter() {
|
||||
self.visit_circuit(circuit)?;
|
||||
}
|
||||
for (_, global_const) in input.global_consts.iter() {
|
||||
self.visit_global_const(global_const)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
x => x.into(),
|
||||
}
|
||||
}
|
||||
}
|
215
asg/src/scope.rs
Normal file
215
asg/src/scope.rs
Normal file
@ -0,0 +1,215 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{AsgContext, AsgConvertError, Circuit, DefinitionStatement, Function, Input, Type, Variable};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
/// An abstract data type that track the current bindings for variables, functions, and circuits.
|
||||
#[derive(Clone)]
|
||||
pub struct Scope<'a> {
|
||||
pub context: AsgContext<'a>,
|
||||
|
||||
/// The unique id of the scope.
|
||||
pub id: u32,
|
||||
|
||||
/// The parent scope that this scope inherits.
|
||||
pub parent_scope: Cell<Option<&'a Scope<'a>>>,
|
||||
|
||||
/// The function definition that this scope occurs in.
|
||||
pub function: Cell<Option<&'a Function<'a>>>,
|
||||
|
||||
/// The circuit definition that this scope occurs in.
|
||||
pub circuit_self: Cell<Option<&'a Circuit<'a>>>,
|
||||
|
||||
/// Maps variable name => variable.
|
||||
pub variables: RefCell<IndexMap<String, &'a Variable<'a>>>,
|
||||
|
||||
/// Maps function name => function.
|
||||
pub functions: RefCell<IndexMap<String, &'a Function<'a>>>,
|
||||
|
||||
/// Maps global constant name => global const code block.
|
||||
pub global_consts: RefCell<IndexMap<String, &'a DefinitionStatement<'a>>>,
|
||||
|
||||
/// Maps circuit name => circuit.
|
||||
pub circuits: RefCell<IndexMap<String, &'a Circuit<'a>>>,
|
||||
|
||||
/// The main input to the program.
|
||||
pub input: Cell<Option<Input<'a>>>,
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
impl<'a> Scope<'a> {
|
||||
///
|
||||
/// Returns a reference to the variable corresponding to the name.
|
||||
///
|
||||
/// If the current scope did not have this name present, then the parent scope is checked.
|
||||
/// If there is no parent scope, then `None` is returned.
|
||||
///
|
||||
pub fn resolve_variable(&self, name: &str) -> Option<&'a Variable<'a>> {
|
||||
if let Some(resolved) = self.variables.borrow().get(name) {
|
||||
Some(*resolved)
|
||||
} else if let Some(scope) = self.parent_scope.get() {
|
||||
scope.resolve_variable(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a reference to the current function.
|
||||
///
|
||||
/// If the current scope did not have a function present, then the parent scope is checked.
|
||||
/// If there is no parent scope, then `None` is returned.
|
||||
///
|
||||
pub fn resolve_current_function(&self) -> Option<&'a Function> {
|
||||
if let Some(resolved) = self.function.get() {
|
||||
Some(resolved)
|
||||
} else if let Some(scope) = self.parent_scope.get() {
|
||||
scope.resolve_current_function()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a reference to the current input.
|
||||
///
|
||||
/// If the current scope did not have an input present, then the parent scope is checked.
|
||||
/// If there is no parent scope, then `None` is returned.
|
||||
///
|
||||
pub fn resolve_input(&self) -> Option<Input<'a>> {
|
||||
if let Some(input) = self.input.get() {
|
||||
Some(input)
|
||||
} else if let Some(resolved) = self.parent_scope.get() {
|
||||
resolved.resolve_input()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a reference to the function corresponding to the name.
|
||||
///
|
||||
/// If the current scope did not have this name present, then the parent scope is checked.
|
||||
/// If there is no parent scope, then `None` is returned.
|
||||
///
|
||||
pub fn resolve_function(&self, name: &str) -> Option<&'a Function<'a>> {
|
||||
if let Some(resolved) = self.functions.borrow().get(name) {
|
||||
Some(*resolved)
|
||||
} else if let Some(resolved) = self.parent_scope.get() {
|
||||
resolved.resolve_function(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a reference to the circuit corresponding to the name.
|
||||
///
|
||||
/// If the current scope did not have this name present, then the parent scope is checked.
|
||||
/// If there is no parent scope, then `None` is returned.
|
||||
///
|
||||
pub fn resolve_circuit(&self, name: &str) -> Option<&'a Circuit<'a>> {
|
||||
if let Some(resolved) = self.circuits.borrow().get(name) {
|
||||
Some(*resolved)
|
||||
} else if name == "Self" && self.circuit_self.get().is_some() {
|
||||
self.circuit_self.get()
|
||||
} else if let Some(resolved) = self.parent_scope.get() {
|
||||
resolved.resolve_circuit(name)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a reference to the current circuit.
|
||||
///
|
||||
/// If the current scope did not have a circuit self present, then the parent scope is checked.
|
||||
/// If there is no parent scope, then `None` is returned.
|
||||
///
|
||||
pub fn resolve_circuit_self(&self) -> Option<&'a Circuit<'a>> {
|
||||
if let Some(resolved) = self.circuit_self.get() {
|
||||
Some(resolved)
|
||||
} else if let Some(resolved) = self.parent_scope.get() {
|
||||
resolved.resolve_circuit_self()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns a new scope given a parent scope.
|
||||
///
|
||||
pub fn make_subscope(self: &'a Scope<'a>) -> &'a Scope<'a> {
|
||||
self.context.alloc_scope(Scope::<'a> {
|
||||
context: self.context,
|
||||
id: self.context.get_id(),
|
||||
parent_scope: Cell::new(Some(self)),
|
||||
circuit_self: Cell::new(None),
|
||||
variables: RefCell::new(IndexMap::new()),
|
||||
functions: RefCell::new(IndexMap::new()),
|
||||
circuits: RefCell::new(IndexMap::new()),
|
||||
global_consts: RefCell::new(IndexMap::new()),
|
||||
function: Cell::new(None),
|
||||
input: Cell::new(None),
|
||||
})
|
||||
}
|
||||
|
||||
///
|
||||
/// Returns the type returned by the current scope.
|
||||
///
|
||||
pub fn resolve_ast_type(&self, type_: &leo_ast::Type) -> Result<Type<'a>, AsgConvertError> {
|
||||
use leo_ast::Type::*;
|
||||
Ok(match type_ {
|
||||
Address => Type::Address,
|
||||
Boolean => Type::Boolean,
|
||||
Field => Type::Field,
|
||||
Group => Type::Group,
|
||||
IntegerType(int_type) => Type::Integer(int_type.clone()),
|
||||
Array(sub_type, dimensions) => {
|
||||
let mut item = Box::new(self.resolve_ast_type(&*sub_type)?);
|
||||
for dimension in dimensions.0.iter().rev() {
|
||||
let dimension = dimension
|
||||
.value
|
||||
.parse::<usize>()
|
||||
.map_err(|_| AsgConvertError::parse_index_error())?;
|
||||
item = Box::new(Type::Array(item, dimension));
|
||||
}
|
||||
*item
|
||||
}
|
||||
Tuple(sub_types) => Type::Tuple(
|
||||
sub_types
|
||||
.iter()
|
||||
.map(|x| self.resolve_ast_type(x))
|
||||
.collect::<Result<Vec<_>, AsgConvertError>>()?,
|
||||
),
|
||||
Circuit(name) if name.name.as_ref() == "Self" => Type::Circuit(
|
||||
self.resolve_circuit_self()
|
||||
.ok_or_else(|| AsgConvertError::unresolved_circuit(&name.name, &name.span))?,
|
||||
),
|
||||
SelfType => Type::Circuit(
|
||||
self.resolve_circuit_self()
|
||||
.ok_or_else(AsgConvertError::reference_self_outside_circuit)?,
|
||||
),
|
||||
Circuit(name) => Type::Circuit(
|
||||
self.resolve_circuit(&name.name)
|
||||
.ok_or_else(|| AsgConvertError::unresolved_circuit(&name.name, &name.span))?,
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
260
asg/src/statement/assign.rs
Normal file
260
asg/src/statement/assign.rs
Normal file
@ -0,0 +1,260 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
AsgConvertError,
|
||||
CircuitMember,
|
||||
ConstInt,
|
||||
ConstValue,
|
||||
Expression,
|
||||
ExpressionNode,
|
||||
FromAst,
|
||||
Identifier,
|
||||
IntegerType,
|
||||
Node,
|
||||
PartialType,
|
||||
Scope,
|
||||
Span,
|
||||
Statement,
|
||||
Type,
|
||||
Variable,
|
||||
};
|
||||
pub use leo_ast::AssignOperation;
|
||||
use leo_ast::AssigneeAccess as AstAssigneeAccess;
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum AssignAccess<'a> {
|
||||
ArrayRange(Cell<Option<&'a Expression<'a>>>, Cell<Option<&'a Expression<'a>>>),
|
||||
ArrayIndex(Cell<&'a Expression<'a>>),
|
||||
Tuple(usize),
|
||||
Member(Identifier),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AssignStatement<'a> {
|
||||
pub parent: Cell<Option<&'a Statement<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub operation: AssignOperation,
|
||||
pub target_variable: Cell<&'a Variable<'a>>,
|
||||
pub target_accesses: Vec<AssignAccess<'a>>,
|
||||
pub value: Cell<&'a Expression<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Node for AssignStatement<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::AssignStatement> for &'a Statement<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
statement: &leo_ast::AssignStatement,
|
||||
_expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<Self, AsgConvertError> {
|
||||
let (name, span) = (&statement.assignee.identifier.name, &statement.assignee.identifier.span);
|
||||
|
||||
let variable = if name.as_ref() == "input" {
|
||||
if let Some(input) = scope.resolve_input() {
|
||||
input.container
|
||||
} else {
|
||||
return Err(AsgConvertError::InternalError(
|
||||
"attempted to reference input when none is in scope".to_string(),
|
||||
));
|
||||
}
|
||||
} else {
|
||||
scope
|
||||
.resolve_variable(&name)
|
||||
.ok_or_else(|| AsgConvertError::unresolved_reference(name, &span))?
|
||||
};
|
||||
|
||||
if !variable.borrow().mutable {
|
||||
return Err(AsgConvertError::immutable_assignment(&name, &statement.span));
|
||||
}
|
||||
let mut target_type: Option<PartialType> = Some(variable.borrow().type_.clone().into());
|
||||
|
||||
let mut target_accesses = vec![];
|
||||
for access in statement.assignee.accesses.iter() {
|
||||
target_accesses.push(match access {
|
||||
AstAssigneeAccess::ArrayRange(left, right) => {
|
||||
let index_type = Some(PartialType::Integer(None, Some(IntegerType::U32)));
|
||||
let left = left
|
||||
.as_ref()
|
||||
.map(
|
||||
|left: &leo_ast::Expression| -> Result<&'a Expression<'a>, AsgConvertError> {
|
||||
<&Expression<'a>>::from_ast(scope, left, index_type.clone())
|
||||
},
|
||||
)
|
||||
.transpose()?;
|
||||
let right = right
|
||||
.as_ref()
|
||||
.map(
|
||||
|right: &leo_ast::Expression| -> Result<&'a Expression<'a>, AsgConvertError> {
|
||||
<&Expression<'a>>::from_ast(scope, right, index_type)
|
||||
},
|
||||
)
|
||||
.transpose()?;
|
||||
|
||||
match &target_type {
|
||||
Some(PartialType::Array(item, len)) => {
|
||||
if let (Some(left), Some(right)) = (
|
||||
left.as_ref()
|
||||
.map(|x| x.const_value())
|
||||
.unwrap_or_else(|| Some(ConstValue::Int(ConstInt::U32(0)))),
|
||||
right
|
||||
.as_ref()
|
||||
.map(|x| x.const_value())
|
||||
.unwrap_or_else(|| Some(ConstValue::Int(ConstInt::U32(len.map(|x| x as u32)?)))),
|
||||
) {
|
||||
let left = match left {
|
||||
ConstValue::Int(x) => x.to_usize().ok_or_else(|| {
|
||||
AsgConvertError::invalid_assign_index(&name, &x.to_string(), &statement.span)
|
||||
})?,
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
let right = match right {
|
||||
ConstValue::Int(x) => x.to_usize().ok_or_else(|| {
|
||||
AsgConvertError::invalid_assign_index(&name, &x.to_string(), &statement.span)
|
||||
})?,
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
if right >= left {
|
||||
target_type = Some(PartialType::Array(item.clone(), Some((right - left) as usize)))
|
||||
} else {
|
||||
return Err(AsgConvertError::invalid_backwards_assignment(
|
||||
&name,
|
||||
left,
|
||||
right,
|
||||
&statement.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => return Err(AsgConvertError::index_into_non_array(&name, &statement.span)),
|
||||
}
|
||||
|
||||
AssignAccess::ArrayRange(Cell::new(left), Cell::new(right))
|
||||
}
|
||||
AstAssigneeAccess::ArrayIndex(index) => {
|
||||
target_type = match target_type.clone() {
|
||||
Some(PartialType::Array(item, _)) => item.map(|x| *x),
|
||||
_ => return Err(AsgConvertError::index_into_non_array(&name, &statement.span)),
|
||||
};
|
||||
AssignAccess::ArrayIndex(Cell::new(<&Expression<'a>>::from_ast(
|
||||
scope,
|
||||
index,
|
||||
Some(PartialType::Integer(None, Some(IntegerType::U32))),
|
||||
)?))
|
||||
}
|
||||
AstAssigneeAccess::Tuple(index, _) => {
|
||||
let index = index
|
||||
.value
|
||||
.parse::<usize>()
|
||||
.map_err(|_| AsgConvertError::parse_index_error())?;
|
||||
target_type = match target_type {
|
||||
Some(PartialType::Tuple(types)) => types
|
||||
.get(index)
|
||||
.cloned()
|
||||
.ok_or_else(|| AsgConvertError::tuple_index_out_of_bounds(index, &statement.span))?,
|
||||
_ => return Err(AsgConvertError::index_into_non_tuple(&name, &statement.span)),
|
||||
};
|
||||
AssignAccess::Tuple(index)
|
||||
}
|
||||
AstAssigneeAccess::Member(name) => {
|
||||
target_type = match target_type {
|
||||
Some(PartialType::Type(Type::Circuit(circuit))) => {
|
||||
let circuit = circuit;
|
||||
|
||||
let members = circuit.members.borrow();
|
||||
let member = members.get(name.name.as_ref()).ok_or_else(|| {
|
||||
AsgConvertError::unresolved_circuit_member(
|
||||
&circuit.name.borrow().name,
|
||||
&name.name,
|
||||
&statement.span,
|
||||
)
|
||||
})?;
|
||||
|
||||
let x = match &member {
|
||||
CircuitMember::Variable(type_) => type_.clone(),
|
||||
CircuitMember::Function(_) => {
|
||||
return Err(AsgConvertError::illegal_function_assign(&name.name, &statement.span));
|
||||
}
|
||||
};
|
||||
Some(x.partial())
|
||||
}
|
||||
_ => {
|
||||
return Err(AsgConvertError::index_into_non_tuple(
|
||||
&statement.assignee.identifier.name,
|
||||
&statement.span,
|
||||
));
|
||||
}
|
||||
};
|
||||
AssignAccess::Member(name.clone())
|
||||
}
|
||||
});
|
||||
}
|
||||
let value = <&Expression<'a>>::from_ast(scope, &statement.value, target_type)?;
|
||||
|
||||
let statement = scope.context.alloc_statement(Statement::Assign(AssignStatement {
|
||||
parent: Cell::new(None),
|
||||
span: Some(statement.span.clone()),
|
||||
operation: statement.operation.clone(),
|
||||
target_variable: Cell::new(variable),
|
||||
target_accesses,
|
||||
value: Cell::new(value),
|
||||
}));
|
||||
|
||||
{
|
||||
let mut variable = variable.borrow_mut();
|
||||
variable.assignments.push(statement);
|
||||
}
|
||||
|
||||
Ok(statement)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::AssignStatement> for &AssignStatement<'a> {
|
||||
fn into(self) -> leo_ast::AssignStatement {
|
||||
leo_ast::AssignStatement {
|
||||
operation: self.operation.clone(),
|
||||
assignee: leo_ast::Assignee {
|
||||
identifier: self.target_variable.get().borrow().name.clone(),
|
||||
accesses: self
|
||||
.target_accesses
|
||||
.iter()
|
||||
.map(|access| match access {
|
||||
AssignAccess::ArrayRange(left, right) => {
|
||||
AstAssigneeAccess::ArrayRange(left.get().map(|e| e.into()), right.get().map(|e| e.into()))
|
||||
}
|
||||
AssignAccess::ArrayIndex(index) => AstAssigneeAccess::ArrayIndex(index.get().into()),
|
||||
AssignAccess::Tuple(index) => AstAssigneeAccess::Tuple(
|
||||
leo_ast::PositiveNumber {
|
||||
value: index.to_string().into(),
|
||||
},
|
||||
self.span.clone().unwrap_or_default(),
|
||||
),
|
||||
AssignAccess::Member(name) => AstAssigneeAccess::Member(name.clone()),
|
||||
})
|
||||
.collect(),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
},
|
||||
value: self.value.get().into(),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
63
asg/src/statement/block.rs
Normal file
63
asg/src/statement/block.rs
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{AsgConvertError, FromAst, Node, PartialType, Scope, Span, Statement};
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BlockStatement<'a> {
|
||||
pub parent: Cell<Option<&'a Statement<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub statements: Vec<Cell<&'a Statement<'a>>>,
|
||||
pub scope: &'a Scope<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Node for BlockStatement<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::Block> for BlockStatement<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
statement: &leo_ast::Block,
|
||||
_expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<Self, AsgConvertError> {
|
||||
let new_scope = scope.make_subscope();
|
||||
|
||||
let mut output = vec![];
|
||||
for item in statement.statements.iter() {
|
||||
output.push(Cell::new(<&'a Statement<'a>>::from_ast(&new_scope, item, None)?));
|
||||
}
|
||||
Ok(BlockStatement {
|
||||
parent: Cell::new(None),
|
||||
span: Some(statement.span.clone()),
|
||||
statements: output,
|
||||
scope: new_scope,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::Block> for &BlockStatement<'a> {
|
||||
fn into(self) -> leo_ast::Block {
|
||||
leo_ast::Block {
|
||||
statements: self.statements.iter().map(|statement| statement.get().into()).collect(),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
78
asg/src/statement/conditional.rs
Normal file
78
asg/src/statement/conditional.rs
Normal file
@ -0,0 +1,78 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{AsgConvertError, BlockStatement, Expression, FromAst, Node, PartialType, Scope, Span, Statement, Type};
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConditionalStatement<'a> {
|
||||
pub parent: Cell<Option<&'a Statement<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub condition: Cell<&'a Expression<'a>>,
|
||||
pub result: Cell<&'a Statement<'a>>,
|
||||
pub next: Cell<Option<&'a Statement<'a>>>,
|
||||
}
|
||||
|
||||
impl<'a> Node for ConditionalStatement<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::ConditionalStatement> for ConditionalStatement<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
statement: &leo_ast::ConditionalStatement,
|
||||
_expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<Self, AsgConvertError> {
|
||||
let condition = <&Expression<'a>>::from_ast(scope, &statement.condition, Some(Type::Boolean.into()))?;
|
||||
let result = scope.context.alloc_statement(Statement::Block(BlockStatement::from_ast(
|
||||
scope,
|
||||
&statement.block,
|
||||
None,
|
||||
)?));
|
||||
let next = statement
|
||||
.next
|
||||
.as_deref()
|
||||
.map(|next| -> Result<&'a Statement<'a>, AsgConvertError> {
|
||||
<&'a Statement<'a>>::from_ast(scope, next, None)
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
Ok(ConditionalStatement {
|
||||
parent: Cell::new(None),
|
||||
span: Some(statement.span.clone()),
|
||||
condition: Cell::new(condition),
|
||||
result: Cell::new(result),
|
||||
next: Cell::new(next),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::ConditionalStatement> for &ConditionalStatement<'a> {
|
||||
fn into(self) -> leo_ast::ConditionalStatement {
|
||||
leo_ast::ConditionalStatement {
|
||||
condition: self.condition.get().into(),
|
||||
block: match self.result.get() {
|
||||
Statement::Block(block) => block.into(),
|
||||
_ => unimplemented!(),
|
||||
},
|
||||
next: self.next.get().as_deref().map(|e| Box::new(e.into())),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
132
asg/src/statement/console.rs
Normal file
132
asg/src/statement/console.rs
Normal file
@ -0,0 +1,132 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{AsgConvertError, Expression, FromAst, Node, PartialType, Scope, Span, Statement, Type};
|
||||
use leo_ast::{ConsoleFunction as AstConsoleFunction, FormatStringPart};
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
// TODO (protryon): Refactor to not require/depend on span
|
||||
#[derive(Clone)]
|
||||
pub struct FormatString<'a> {
|
||||
pub parts: Vec<FormatStringPart>,
|
||||
pub parameters: Vec<Cell<&'a Expression<'a>>>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ConsoleFunction<'a> {
|
||||
Assert(Cell<&'a Expression<'a>>),
|
||||
Debug(FormatString<'a>),
|
||||
Error(FormatString<'a>),
|
||||
Log(FormatString<'a>),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConsoleStatement<'a> {
|
||||
pub parent: Cell<Option<&'a Statement<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub function: ConsoleFunction<'a>,
|
||||
}
|
||||
|
||||
impl<'a> Node for ConsoleStatement<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::FormatString> for FormatString<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &leo_ast::FormatString,
|
||||
_expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<Self, AsgConvertError> {
|
||||
let expected_param_len = value
|
||||
.parts
|
||||
.iter()
|
||||
.filter(|x| matches!(x, FormatStringPart::Container))
|
||||
.count();
|
||||
if value.parameters.len() != expected_param_len {
|
||||
// + 1 for formatting string as to not confuse user
|
||||
return Err(AsgConvertError::unexpected_call_argument_count(
|
||||
expected_param_len + 1,
|
||||
value.parameters.len() + 1,
|
||||
&value.span,
|
||||
));
|
||||
}
|
||||
let mut parameters = vec![];
|
||||
for parameter in value.parameters.iter() {
|
||||
parameters.push(Cell::new(<&Expression<'a>>::from_ast(scope, parameter, None)?));
|
||||
}
|
||||
Ok(FormatString {
|
||||
parts: value.parts.clone(),
|
||||
parameters,
|
||||
span: value.span.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::FormatString> for &FormatString<'a> {
|
||||
fn into(self) -> leo_ast::FormatString {
|
||||
leo_ast::FormatString {
|
||||
parts: self.parts.clone(),
|
||||
parameters: self.parameters.iter().map(|e| e.get().into()).collect(),
|
||||
span: self.span.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::ConsoleStatement> for ConsoleStatement<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
statement: &leo_ast::ConsoleStatement,
|
||||
_expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<Self, AsgConvertError> {
|
||||
Ok(ConsoleStatement {
|
||||
parent: Cell::new(None),
|
||||
span: Some(statement.span.clone()),
|
||||
function: match &statement.function {
|
||||
AstConsoleFunction::Assert(expression) => ConsoleFunction::Assert(Cell::new(
|
||||
<&Expression<'a>>::from_ast(scope, expression, Some(Type::Boolean.into()))?,
|
||||
)),
|
||||
AstConsoleFunction::Debug(formatted_string) => {
|
||||
ConsoleFunction::Debug(FormatString::from_ast(scope, formatted_string, None)?)
|
||||
}
|
||||
AstConsoleFunction::Error(formatted_string) => {
|
||||
ConsoleFunction::Error(FormatString::from_ast(scope, formatted_string, None)?)
|
||||
}
|
||||
AstConsoleFunction::Log(formatted_string) => {
|
||||
ConsoleFunction::Log(FormatString::from_ast(scope, formatted_string, None)?)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::ConsoleStatement> for &ConsoleStatement<'a> {
|
||||
fn into(self) -> leo_ast::ConsoleStatement {
|
||||
use ConsoleFunction::*;
|
||||
leo_ast::ConsoleStatement {
|
||||
function: match &self.function {
|
||||
Assert(e) => AstConsoleFunction::Assert(e.get().into()),
|
||||
Debug(formatted_string) => AstConsoleFunction::Debug(formatted_string.into()),
|
||||
Error(formatted_string) => AstConsoleFunction::Error(formatted_string.into()),
|
||||
Log(formatted_string) => AstConsoleFunction::Log(formatted_string.into()),
|
||||
},
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
171
asg/src/statement/definition.rs
Normal file
171
asg/src/statement/definition.rs
Normal file
@ -0,0 +1,171 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
AsgConvertError,
|
||||
Expression,
|
||||
ExpressionNode,
|
||||
FromAst,
|
||||
InnerVariable,
|
||||
Node,
|
||||
PartialType,
|
||||
Scope,
|
||||
Span,
|
||||
Statement,
|
||||
Type,
|
||||
Variable,
|
||||
};
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DefinitionStatement<'a> {
|
||||
pub parent: Cell<Option<&'a Statement<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub variables: Vec<&'a Variable<'a>>,
|
||||
pub value: Cell<&'a Expression<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> DefinitionStatement<'a> {
|
||||
pub fn split(&self) -> Vec<(String, Self)> {
|
||||
self.variables
|
||||
.iter()
|
||||
.map(|variable| {
|
||||
(variable.borrow().name.name.to_string(), DefinitionStatement {
|
||||
parent: self.parent.clone(),
|
||||
span: self.span.clone(),
|
||||
variables: vec![variable],
|
||||
value: self.value.clone(),
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Node for DefinitionStatement<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::DefinitionStatement> for &'a Statement<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
statement: &leo_ast::DefinitionStatement,
|
||||
_expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<Self, AsgConvertError> {
|
||||
let type_ = statement
|
||||
.type_
|
||||
.as_ref()
|
||||
.map(|x| scope.resolve_ast_type(&x))
|
||||
.transpose()?;
|
||||
|
||||
let value = <&Expression<'a>>::from_ast(scope, &statement.value, type_.clone().map(Into::into))?;
|
||||
|
||||
let type_ = type_.or_else(|| value.get_type());
|
||||
|
||||
let mut output_types = vec![];
|
||||
|
||||
let mut variables = vec![];
|
||||
if statement.variable_names.is_empty() {
|
||||
return Err(AsgConvertError::illegal_ast_structure(
|
||||
"cannot have 0 variable names in destructuring tuple",
|
||||
));
|
||||
}
|
||||
if statement.variable_names.len() == 1 {
|
||||
// any return type is fine
|
||||
output_types.push(type_);
|
||||
} else {
|
||||
// tuple destructure
|
||||
match type_.as_ref() {
|
||||
Some(Type::Tuple(sub_types)) if sub_types.len() == statement.variable_names.len() => {
|
||||
output_types.extend(sub_types.clone().into_iter().map(Some).collect::<Vec<_>>());
|
||||
}
|
||||
type_ => {
|
||||
return Err(AsgConvertError::unexpected_type(
|
||||
&*format!("{}-ary tuple", statement.variable_names.len()),
|
||||
type_.map(|x| x.to_string()).as_deref(),
|
||||
&statement.span,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (variable, type_) in statement.variable_names.iter().zip(output_types.into_iter()) {
|
||||
variables.push(&*scope.context.alloc_variable(RefCell::new(InnerVariable {
|
||||
id: scope.context.get_id(),
|
||||
name: variable.identifier.clone(),
|
||||
type_:
|
||||
type_.ok_or_else(|| AsgConvertError::unresolved_type(&variable.identifier.name, &statement.span))?,
|
||||
mutable: variable.mutable,
|
||||
const_: false,
|
||||
declaration: crate::VariableDeclaration::Definition,
|
||||
references: vec![],
|
||||
assignments: vec![],
|
||||
})));
|
||||
}
|
||||
|
||||
for variable in variables.iter() {
|
||||
scope
|
||||
.variables
|
||||
.borrow_mut()
|
||||
.insert(variable.borrow().name.name.to_string(), *variable);
|
||||
}
|
||||
|
||||
let statement = scope
|
||||
.context
|
||||
.alloc_statement(Statement::Definition(DefinitionStatement {
|
||||
parent: Cell::new(None),
|
||||
span: Some(statement.span.clone()),
|
||||
variables: variables.clone(),
|
||||
value: Cell::new(value),
|
||||
}));
|
||||
|
||||
for variable in variables {
|
||||
variable.borrow_mut().assignments.push(statement);
|
||||
}
|
||||
|
||||
Ok(statement)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::DefinitionStatement> for &DefinitionStatement<'a> {
|
||||
fn into(self) -> leo_ast::DefinitionStatement {
|
||||
assert!(!self.variables.is_empty());
|
||||
|
||||
let mut variable_names = vec![];
|
||||
let mut type_ = None::<leo_ast::Type>;
|
||||
for variable in self.variables.iter() {
|
||||
let variable = variable.borrow();
|
||||
variable_names.push(leo_ast::VariableName {
|
||||
mutable: variable.mutable,
|
||||
identifier: variable.name.clone(),
|
||||
span: variable.name.span.clone(),
|
||||
});
|
||||
if type_.is_none() {
|
||||
type_ = Some((&variable.type_.clone()).into());
|
||||
}
|
||||
}
|
||||
|
||||
leo_ast::DefinitionStatement {
|
||||
declaration_type: leo_ast::Declare::Let,
|
||||
variable_names,
|
||||
type_,
|
||||
value: self.value.get().into(),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
57
asg/src/statement/expression.rs
Normal file
57
asg/src/statement/expression.rs
Normal file
@ -0,0 +1,57 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{AsgConvertError, Expression, FromAst, Node, PartialType, Scope, Span, Statement};
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ExpressionStatement<'a> {
|
||||
pub parent: Cell<Option<&'a Statement<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub expression: Cell<&'a Expression<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Node for ExpressionStatement<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::ExpressionStatement> for ExpressionStatement<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
statement: &leo_ast::ExpressionStatement,
|
||||
_expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<Self, AsgConvertError> {
|
||||
let expression = <&Expression<'a>>::from_ast(scope, &statement.expression, None)?;
|
||||
|
||||
Ok(ExpressionStatement {
|
||||
parent: Cell::new(None),
|
||||
span: Some(statement.span.clone()),
|
||||
expression: Cell::new(expression),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::ExpressionStatement> for &ExpressionStatement<'a> {
|
||||
fn into(self) -> leo_ast::ExpressionStatement {
|
||||
leo_ast::ExpressionStatement {
|
||||
expression: self.expression.get().into(),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
124
asg/src/statement/iteration.rs
Normal file
124
asg/src/statement/iteration.rs
Normal file
@ -0,0 +1,124 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use leo_ast::IntegerType;
|
||||
|
||||
use crate::{
|
||||
AsgConvertError,
|
||||
Expression,
|
||||
ExpressionNode,
|
||||
FromAst,
|
||||
InnerVariable,
|
||||
Node,
|
||||
PartialType,
|
||||
Scope,
|
||||
Span,
|
||||
Statement,
|
||||
Variable,
|
||||
};
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct IterationStatement<'a> {
|
||||
pub parent: Cell<Option<&'a Statement<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub variable: &'a Variable<'a>,
|
||||
pub start: Cell<&'a Expression<'a>>,
|
||||
pub stop: Cell<&'a Expression<'a>>,
|
||||
pub body: Cell<&'a Statement<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Node for IterationStatement<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::IterationStatement> for &'a Statement<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
statement: &leo_ast::IterationStatement,
|
||||
_expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<Self, AsgConvertError> {
|
||||
let expected_index_type = Some(PartialType::Integer(Some(IntegerType::U32), None));
|
||||
let start = <&Expression<'a>>::from_ast(scope, &statement.start, expected_index_type.clone())?;
|
||||
let stop = <&Expression<'a>>::from_ast(scope, &statement.stop, expected_index_type)?;
|
||||
|
||||
// Return an error if start or stop is not constant.
|
||||
if !start.is_consty() {
|
||||
return Err(AsgConvertError::unexpected_nonconst(
|
||||
&start.span().cloned().unwrap_or_default(),
|
||||
));
|
||||
}
|
||||
if !stop.is_consty() {
|
||||
return Err(AsgConvertError::unexpected_nonconst(
|
||||
&stop.span().cloned().unwrap_or_default(),
|
||||
));
|
||||
}
|
||||
|
||||
let variable = scope.context.alloc_variable(RefCell::new(InnerVariable {
|
||||
id: scope.context.get_id(),
|
||||
name: statement.variable.clone(),
|
||||
type_: start
|
||||
.get_type()
|
||||
.ok_or_else(|| AsgConvertError::unresolved_type(&statement.variable.name, &statement.span))?,
|
||||
mutable: false,
|
||||
const_: true,
|
||||
declaration: crate::VariableDeclaration::IterationDefinition,
|
||||
references: vec![],
|
||||
assignments: vec![],
|
||||
}));
|
||||
scope
|
||||
.variables
|
||||
.borrow_mut()
|
||||
.insert(statement.variable.name.to_string(), variable);
|
||||
|
||||
let statement = scope.context.alloc_statement(Statement::Iteration(IterationStatement {
|
||||
parent: Cell::new(None),
|
||||
span: Some(statement.span.clone()),
|
||||
variable,
|
||||
stop: Cell::new(stop),
|
||||
start: Cell::new(start),
|
||||
body: Cell::new(
|
||||
scope
|
||||
.context
|
||||
.alloc_statement(Statement::Block(crate::BlockStatement::from_ast(
|
||||
scope,
|
||||
&statement.block,
|
||||
None,
|
||||
)?)),
|
||||
),
|
||||
}));
|
||||
variable.borrow_mut().assignments.push(statement);
|
||||
Ok(statement)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::IterationStatement> for &IterationStatement<'a> {
|
||||
fn into(self) -> leo_ast::IterationStatement {
|
||||
leo_ast::IterationStatement {
|
||||
variable: self.variable.borrow().name.clone(),
|
||||
start: self.start.get().into(),
|
||||
stop: self.stop.get().into(),
|
||||
block: match self.body.get() {
|
||||
Statement::Block(block) => block.into(),
|
||||
_ => unimplemented!(),
|
||||
},
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
130
asg/src/statement/mod.rs
Normal file
130
asg/src/statement/mod.rs
Normal file
@ -0,0 +1,130 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! This module defines a statement node in an asg.
|
||||
//!
|
||||
//! Ast statement nodes can be directly converted into asg nodes with no major differences.
|
||||
|
||||
mod assign;
|
||||
pub use assign::*;
|
||||
|
||||
mod block;
|
||||
pub use block::*;
|
||||
|
||||
mod conditional;
|
||||
pub use conditional::*;
|
||||
|
||||
mod console;
|
||||
pub use console::*;
|
||||
|
||||
mod definition;
|
||||
pub use definition::*;
|
||||
|
||||
mod expression;
|
||||
pub use expression::*;
|
||||
|
||||
mod iteration;
|
||||
pub use iteration::*;
|
||||
|
||||
mod return_;
|
||||
pub use return_::*;
|
||||
|
||||
use crate::{AsgConvertError, FromAst, Node, PartialType, Scope, Span};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Statement<'a> {
|
||||
Return(ReturnStatement<'a>),
|
||||
Definition(DefinitionStatement<'a>),
|
||||
Assign(AssignStatement<'a>),
|
||||
Conditional(ConditionalStatement<'a>),
|
||||
Iteration(IterationStatement<'a>),
|
||||
Console(ConsoleStatement<'a>),
|
||||
Expression(ExpressionStatement<'a>),
|
||||
Block(BlockStatement<'a>),
|
||||
Empty(Option<Span>),
|
||||
}
|
||||
|
||||
impl<'a> Node for Statement<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
use Statement::*;
|
||||
match self {
|
||||
Return(s) => s.span(),
|
||||
Definition(s) => s.span(),
|
||||
Assign(s) => s.span(),
|
||||
Conditional(s) => s.span(),
|
||||
Iteration(s) => s.span(),
|
||||
Console(s) => s.span(),
|
||||
Expression(s) => s.span(),
|
||||
Block(s) => s.span(),
|
||||
Empty(s) => s.as_ref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::Statement> for &'a Statement<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
value: &leo_ast::Statement,
|
||||
_expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<&'a Statement<'a>, AsgConvertError> {
|
||||
use leo_ast::Statement::*;
|
||||
Ok(match value {
|
||||
Return(statement) => scope
|
||||
.context
|
||||
.alloc_statement(Statement::Return(ReturnStatement::from_ast(scope, statement, None)?)),
|
||||
Definition(statement) => Self::from_ast(scope, statement, None)?,
|
||||
Assign(statement) => Self::from_ast(scope, statement, None)?,
|
||||
Conditional(statement) => {
|
||||
scope
|
||||
.context
|
||||
.alloc_statement(Statement::Conditional(ConditionalStatement::from_ast(
|
||||
scope, statement, None,
|
||||
)?))
|
||||
}
|
||||
Iteration(statement) => Self::from_ast(scope, statement, None)?,
|
||||
Console(statement) => scope
|
||||
.context
|
||||
.alloc_statement(Statement::Console(ConsoleStatement::from_ast(scope, statement, None)?)),
|
||||
Expression(statement) => {
|
||||
scope
|
||||
.context
|
||||
.alloc_statement(Statement::Expression(ExpressionStatement::from_ast(
|
||||
scope, statement, None,
|
||||
)?))
|
||||
}
|
||||
Block(statement) => scope
|
||||
.context
|
||||
.alloc_statement(Statement::Block(BlockStatement::from_ast(scope, statement, None)?)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::Statement> for &Statement<'a> {
|
||||
fn into(self) -> leo_ast::Statement {
|
||||
use Statement::*;
|
||||
match self {
|
||||
Return(statement) => leo_ast::Statement::Return(statement.into()),
|
||||
Definition(statement) => leo_ast::Statement::Definition(statement.into()),
|
||||
Assign(statement) => leo_ast::Statement::Assign(statement.into()),
|
||||
Conditional(statement) => leo_ast::Statement::Conditional(statement.into()),
|
||||
Iteration(statement) => leo_ast::Statement::Iteration(statement.into()),
|
||||
Console(statement) => leo_ast::Statement::Console(statement.into()),
|
||||
Expression(statement) => leo_ast::Statement::Expression(statement.into()),
|
||||
Block(statement) => leo_ast::Statement::Block(statement.into()),
|
||||
Empty(_) => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
62
asg/src/statement/return_.rs
Normal file
62
asg/src/statement/return_.rs
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{AsgConvertError, Expression, FromAst, Node, PartialType, Scope, Span, Statement, Type};
|
||||
|
||||
use std::cell::Cell;
|
||||
#[derive(Clone)]
|
||||
pub struct ReturnStatement<'a> {
|
||||
pub parent: Cell<Option<&'a Statement<'a>>>,
|
||||
pub span: Option<Span>,
|
||||
pub expression: Cell<&'a Expression<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Node for ReturnStatement<'a> {
|
||||
fn span(&self) -> Option<&Span> {
|
||||
self.span.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromAst<'a, leo_ast::ReturnStatement> for ReturnStatement<'a> {
|
||||
fn from_ast(
|
||||
scope: &'a Scope<'a>,
|
||||
statement: &leo_ast::ReturnStatement,
|
||||
_expected_type: Option<PartialType<'a>>,
|
||||
) -> Result<Self, AsgConvertError> {
|
||||
let return_type: Option<Type> = scope
|
||||
.resolve_current_function()
|
||||
.map(|x| x.output.clone())
|
||||
.map(Into::into);
|
||||
Ok(ReturnStatement {
|
||||
parent: Cell::new(None),
|
||||
span: Some(statement.span.clone()),
|
||||
expression: Cell::new(<&Expression<'a>>::from_ast(
|
||||
scope,
|
||||
&statement.expression,
|
||||
return_type.map(Into::into),
|
||||
)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::ReturnStatement> for &ReturnStatement<'a> {
|
||||
fn into(self) -> leo_ast::ReturnStatement {
|
||||
leo_ast::ReturnStatement {
|
||||
expression: self.expression.get().into(),
|
||||
span: self.span.clone().unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
215
asg/src/type_.rs
Normal file
215
asg/src/type_.rs
Normal file
@ -0,0 +1,215 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::Circuit;
|
||||
pub use leo_ast::IntegerType;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// A type in an asg.
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum Type<'a> {
|
||||
// Data types
|
||||
Address,
|
||||
Boolean,
|
||||
Field,
|
||||
Group,
|
||||
Integer(IntegerType),
|
||||
|
||||
// Data type wrappers
|
||||
Array(Box<Type<'a>>, usize),
|
||||
Tuple(Vec<Type<'a>>),
|
||||
Circuit(&'a Circuit<'a>),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub enum PartialType<'a> {
|
||||
Type(Type<'a>), // non-array or tuple
|
||||
Integer(Option<IntegerType>, Option<IntegerType>), // specific, context-specific
|
||||
Array(Option<Box<PartialType<'a>>>, Option<usize>),
|
||||
Tuple(Vec<Option<PartialType<'a>>>),
|
||||
}
|
||||
|
||||
impl<'a> Into<Option<Type<'a>>> for PartialType<'a> {
|
||||
fn into(self) -> Option<Type<'a>> {
|
||||
match self {
|
||||
PartialType::Type(t) => Some(t),
|
||||
PartialType::Integer(sub_type, contextual_type) => Some(Type::Integer(sub_type.or(contextual_type)?)),
|
||||
PartialType::Array(element, len) => Some(Type::Array(Box::new((*element?).full()?), len?)),
|
||||
PartialType::Tuple(sub_types) => Some(Type::Tuple(
|
||||
sub_types
|
||||
.into_iter()
|
||||
.map(|x| x.map(|x| x.full()).flatten())
|
||||
.collect::<Option<Vec<Type>>>()?,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialType<'a> {
|
||||
pub fn full(self) -> Option<Type<'a>> {
|
||||
self.into()
|
||||
}
|
||||
|
||||
pub fn matches(&self, other: &Type<'a>) -> bool {
|
||||
match (self, other) {
|
||||
(PartialType::Type(t), other) => t.is_assignable_from(other),
|
||||
(PartialType::Integer(self_sub_type, _), Type::Integer(sub_type)) => {
|
||||
self_sub_type.as_ref().map(|x| x == sub_type).unwrap_or(true)
|
||||
}
|
||||
(PartialType::Array(element, len), Type::Array(other_element, other_len)) => {
|
||||
if let Some(element) = element {
|
||||
if !element.matches(&*other_element) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if let Some(len) = len {
|
||||
return len == other_len;
|
||||
}
|
||||
true
|
||||
}
|
||||
(PartialType::Tuple(sub_types), Type::Tuple(other_sub_types)) => {
|
||||
// we dont enforce exact length for tuples here (relying on prior type checking) to allow for full-context-free tuple indexing
|
||||
if sub_types.len() > other_sub_types.len() {
|
||||
return false;
|
||||
}
|
||||
for (sub_type, other_sub_type) in sub_types.iter().zip(other_sub_types.iter()) {
|
||||
if let Some(sub_type) = sub_type {
|
||||
if !sub_type.matches(other_sub_type) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<PartialType<'a>> for Type<'a> {
|
||||
fn into(self) -> PartialType<'a> {
|
||||
match self {
|
||||
Type::Integer(sub_type) => PartialType::Integer(Some(sub_type), None),
|
||||
Type::Array(element, len) => PartialType::Array(Some(Box::new((*element).into())), Some(len)),
|
||||
Type::Tuple(sub_types) => PartialType::Tuple(sub_types.into_iter().map(Into::into).map(Some).collect()),
|
||||
x => PartialType::Type(x),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Type<'a> {
|
||||
pub fn is_assignable_from(&self, from: &Type<'a>) -> bool {
|
||||
self == from
|
||||
}
|
||||
|
||||
pub fn partial(self) -> PartialType<'a> {
|
||||
self.into()
|
||||
}
|
||||
|
||||
pub fn is_unit(&self) -> bool {
|
||||
matches!(self, Type::Tuple(t) if t.is_empty())
|
||||
}
|
||||
|
||||
pub fn can_cast_to(&self, to: &Type<'a>) -> bool {
|
||||
matches!(self, Type::Integer(_)) && matches!(to, Type::Integer(_))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for Type<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Type::Address => write!(f, "address"),
|
||||
Type::Boolean => write!(f, "bool"),
|
||||
Type::Field => write!(f, "field"),
|
||||
Type::Group => write!(f, "group"),
|
||||
Type::Integer(sub_type) => sub_type.fmt(f),
|
||||
Type::Array(sub_type, len) => write!(f, "[{}; {}]", sub_type, len),
|
||||
Type::Tuple(sub_types) => {
|
||||
write!(f, "(")?;
|
||||
for (i, sub_type) in sub_types.iter().enumerate() {
|
||||
write!(f, "{}", sub_type)?;
|
||||
if i < sub_types.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
Type::Circuit(circuit) => write!(f, "{}", &circuit.name.borrow().name),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for PartialType<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
PartialType::Type(t) => t.fmt(f),
|
||||
PartialType::Integer(Some(sub_type), _) => write!(f, "{}", sub_type),
|
||||
PartialType::Integer(_, Some(sub_type)) => write!(f, "<{}>", sub_type),
|
||||
PartialType::Integer(_, _) => write!(f, "integer"),
|
||||
PartialType::Array(sub_type, len) => {
|
||||
write!(f, "[")?;
|
||||
if let Some(sub_type) = sub_type {
|
||||
write!(f, "{}", *sub_type)?;
|
||||
} else {
|
||||
write!(f, "?")?;
|
||||
}
|
||||
write!(f, "; ")?;
|
||||
if let Some(len) = len {
|
||||
write!(f, "{}", len)?;
|
||||
} else {
|
||||
write!(f, "?")?;
|
||||
}
|
||||
write!(f, "]")
|
||||
}
|
||||
PartialType::Tuple(sub_types) => {
|
||||
write!(f, "(")?;
|
||||
for (i, sub_type) in sub_types.iter().enumerate() {
|
||||
if let Some(sub_type) = sub_type {
|
||||
write!(f, "{}", *sub_type)?;
|
||||
} else {
|
||||
write!(f, "?")?;
|
||||
}
|
||||
if i < sub_types.len() - 1 {
|
||||
write!(f, ", ")?;
|
||||
}
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Into<leo_ast::Type> for &Type<'a> {
|
||||
fn into(self) -> leo_ast::Type {
|
||||
use Type::*;
|
||||
match self {
|
||||
Address => leo_ast::Type::Address,
|
||||
Boolean => leo_ast::Type::Boolean,
|
||||
Field => leo_ast::Type::Field,
|
||||
Group => leo_ast::Type::Group,
|
||||
Integer(int_type) => leo_ast::Type::IntegerType(int_type.clone()),
|
||||
Array(type_, len) => leo_ast::Type::Array(
|
||||
Box::new(type_.as_ref().into()),
|
||||
leo_ast::ArrayDimensions(vec![leo_ast::PositiveNumber {
|
||||
value: len.to_string().into(),
|
||||
}]),
|
||||
),
|
||||
Tuple(subtypes) => leo_ast::Type::Tuple(subtypes.iter().map(Into::into).collect()),
|
||||
Circuit(circuit) => leo_ast::Type::Circuit(circuit.name.borrow().clone()),
|
||||
}
|
||||
}
|
||||
}
|
44
asg/src/variable.rs
Normal file
44
asg/src/variable.rs
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
||||
use crate::{Expression, Statement, Type};
|
||||
use leo_ast::Identifier;
|
||||
|
||||
/// Specifies how a program variable was declared.
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub enum VariableDeclaration {
|
||||
Definition,
|
||||
IterationDefinition,
|
||||
Parameter,
|
||||
Input,
|
||||
}
|
||||
|
||||
/// Stores information on a program variable.
|
||||
#[derive(Clone)]
|
||||
pub struct InnerVariable<'a> {
|
||||
pub id: u32,
|
||||
pub name: Identifier,
|
||||
pub type_: Type<'a>,
|
||||
pub mutable: bool,
|
||||
pub const_: bool, // only function arguments, const var definitions NOT included
|
||||
pub declaration: VariableDeclaration,
|
||||
pub references: Vec<&'a Expression<'a>>, // all Expression::VariableRef or panic
|
||||
pub assignments: Vec<&'a Statement<'a>>, // all Statement::Assign or panic -- must be 1 if not mutable, or 0 if declaration == input | parameter
|
||||
}
|
||||
|
||||
pub type Variable<'a> = RefCell<InnerVariable<'a>>;
|
3
asg/tests/fail/address/implicit_invalid.leo
Normal file
3
asg/tests/fail/address/implicit_invalid.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main() {
|
||||
const public_key_string: address = zleo1qnr4dkkvkgfqph0vzc3y6z2eu975wnpz2925ntjccd5cfqxtyu8sta57j8;
|
||||
}
|
23
asg/tests/fail/address/mod.rs
Normal file
23
asg/tests/fail/address/mod.rs
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::load_asg;
|
||||
|
||||
#[test]
|
||||
fn test_implicit_invalid() {
|
||||
let program_string = include_str!("implicit_invalid.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
3
asg/tests/fail/array/initializer_fail.leo
Normal file
3
asg/tests/fail/array/initializer_fail.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main(a: [u8; 3]) {
|
||||
console.assert(a == [1u8; -3]);
|
||||
}
|
3
asg/tests/fail/array/input_nested_3x2_fail.leo
Normal file
3
asg/tests/fail/array/input_nested_3x2_fail.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main(a: [u8; (3, 2)]) {
|
||||
console.assert(a == [[0u8; 2]; 3)]); // This should be written the right way as this test is for the input file.
|
||||
}
|
3
asg/tests/fail/array/input_tuple_3x2_fail.leo
Normal file
3
asg/tests/fail/array/input_tuple_3x2_fail.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main(a: [u8; (3, 2)]) {
|
||||
console.assert(a == [0u8; (2, 3)]);
|
||||
}
|
135
asg/tests/fail/array/mod.rs
Normal file
135
asg/tests/fail/array/mod.rs
Normal file
@ -0,0 +1,135 @@
|
||||
// Copyright (C) 2019-2021 Aleo Systems Inc.
|
||||
// This file is part of the Leo library.
|
||||
|
||||
// The Leo library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// The Leo library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::load_asg;
|
||||
|
||||
// Expressions
|
||||
|
||||
#[test]
|
||||
fn test_initializer_fail() {
|
||||
let program_string = include_str!("initializer_fail.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_input_nested_3x2_fail() {
|
||||
let program_string = include_str!("input_nested_3x2_fail.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_input_tuple_3x2_fail() {
|
||||
let program_string = include_str!("input_tuple_3x2_fail.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_fail_initializer() {
|
||||
let program_string = include_str!("multi_fail_initializer.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_inline_fail() {
|
||||
let program_string = include_str!("multi_fail_inline.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multi_initializer_fail() {
|
||||
let program_string = include_str!("multi_initializer_fail.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_3x2_value_fail() {
|
||||
let program_string = include_str!("nested_3x2_value_fail.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tuple_3x2_value_fail() {
|
||||
let program_string = include_str!("tuple_3x2_value_fail.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
||||
|
||||
// Array type tests
|
||||
|
||||
#[test]
|
||||
fn test_type_fail() {
|
||||
let program_string = include_str!("type_fail.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type_nested_value_nested_3x2_fail() {
|
||||
let program_string = include_str!("type_nested_value_nested_3x2_fail.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type_nested_value_nested_4x3x2_fail() {
|
||||
let program_string = include_str!("type_nested_value_nested_4x3x2_fail.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type_nested_value_tuple_3x2_fail() {
|
||||
let program_string = include_str!("type_nested_value_tuple_3x2_fail.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type_nested_value_tuple_4x3x2_fail() {
|
||||
let program_string = include_str!("type_nested_value_tuple_4x3x2_fail.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type_tuple_value_nested_3x2_fail() {
|
||||
let program_string = include_str!("type_tuple_value_nested_3x2_fail.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type_tuple_value_nested_3x2_swap_fail() {
|
||||
let program_string = include_str!("type_tuple_value_nested_3x2_swap_fail.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type_tuple_value_nested_4x3x2_fail() {
|
||||
let program_string = include_str!("type_tuple_value_nested_4x3x2_fail.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type_tuple_value_tuple_3x2_fail() {
|
||||
let program_string = include_str!("type_tuple_value_tuple_3x2_fail.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type_tuple_value_tuple_3x2_swap_fail() {
|
||||
let program_string = include_str!("type_tuple_value_tuple_3x2_swap_fail.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_type_tuple_value_tuple_4x3x2_fail() {
|
||||
let program_string = include_str!("type_tuple_value_tuple_4x3x2_fail.leo");
|
||||
load_asg(program_string).err().unwrap();
|
||||
}
|
3
asg/tests/fail/array/multi_fail_initializer.leo
Normal file
3
asg/tests/fail/array/multi_fail_initializer.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main() {
|
||||
const arr: [u8; (2, 2)] = [[1u8; 2]; 1]; // incorrect dimensions
|
||||
}
|
4
asg/tests/fail/array/multi_fail_inline.leo
Normal file
4
asg/tests/fail/array/multi_fail_inline.leo
Normal file
@ -0,0 +1,4 @@
|
||||
function main() {
|
||||
const arr: [u8; (2, 2)] = [[1u8, 1u8],
|
||||
[1u8]]; // incorrect dimensions
|
||||
}
|
3
asg/tests/fail/array/multi_initializer_fail.leo
Normal file
3
asg/tests/fail/array/multi_initializer_fail.leo
Normal file
@ -0,0 +1,3 @@
|
||||
function main() {
|
||||
const arr: [u8; (2, 2)] = [1u8; (2, 1)]; // incorrect dimensions
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user